[PD] add option to create tapered Pad / Pocket
This PR adds the same functionality as provided by Part Extrude. The used code parts are sorted out to a new helper function that is used by Part and PartDesign.
This commit is contained in:
@@ -425,6 +425,8 @@ SET(Part_SRCS
|
||||
BSplineCurveBiArcs.cpp
|
||||
CrossSection.cpp
|
||||
CrossSection.h
|
||||
ExtrusionHelper.cpp
|
||||
ExtrusionHelper.h
|
||||
GeometryExtension.cpp
|
||||
GeometryExtension.h
|
||||
GeometryDefaultExtension.cpp
|
||||
|
||||
508
src/Mod/Part/App/ExtrusionHelper.cpp
Normal file
508
src/Mod/Part/App/ExtrusionHelper.cpp
Normal file
@@ -0,0 +1,508 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2022 Uwe Stöhr <uwestoehr@lyx.org> *
|
||||
* *
|
||||
* 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"
|
||||
#ifndef _PreComp_
|
||||
# include <cmath>
|
||||
# include <BRepAlgoAPI_Cut.hxx>
|
||||
# include <BRepBuilderAPI_MakeWire.hxx>
|
||||
# include <BRepBuilderAPI_MakeFace.hxx>
|
||||
# include <BRepBuilderAPI_Sewing.hxx>
|
||||
# include <BRepBuilderAPI_MakeSolid.hxx>
|
||||
# include <BRepClass3d_SolidClassifier.hxx>
|
||||
# include <BRepGProp.hxx>
|
||||
# include <BRepOffsetAPI_MakeOffset.hxx>
|
||||
# include <BRepOffsetAPI_ThruSections.hxx>
|
||||
# include <BRepPrimAPI_MakePrism.hxx>
|
||||
# include <gp_Dir.hxx>
|
||||
# include <gp_Trsf.hxx>
|
||||
# include <GProp_GProps.hxx>
|
||||
# include <Precision.hxx>
|
||||
# include <ShapeAnalysis.hxx>
|
||||
# include <ShapeFix_Wire.hxx>
|
||||
# include <TopoDS.hxx>
|
||||
# include <TopoDS_Iterator.hxx>
|
||||
# include <TopExp.hxx>
|
||||
# include <TopExp_Explorer.hxx>
|
||||
#endif
|
||||
|
||||
#include "ExtrusionHelper.h"
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/Tools.h>
|
||||
#include "FeatureExtrusion.h"
|
||||
|
||||
using namespace Part;
|
||||
|
||||
ExtrusionHelper::ExtrusionHelper()
|
||||
{
|
||||
}
|
||||
|
||||
void ExtrusionHelper::makeDraft(const TopoDS_Shape& shape,
|
||||
const gp_Dir& direction,
|
||||
const double LengthFwd,
|
||||
const double LengthRev,
|
||||
const double AngleFwd,
|
||||
const double AngleRev,
|
||||
bool isSolid,
|
||||
std::list<TopoDS_Shape>& drafts,
|
||||
bool isPartDesign)
|
||||
{
|
||||
std::vector<std::vector<TopoDS_Shape>> wiresections;
|
||||
|
||||
auto addWiresToWireSections =
|
||||
[&shape](std::vector<std::vector<TopoDS_Shape>>& wiresections) -> size_t {
|
||||
TopExp_Explorer ex;
|
||||
size_t i = 0;
|
||||
for (ex.Init(shape, TopAbs_WIRE); ex.More(); ex.Next(), ++i) {
|
||||
wiresections.push_back(std::vector<TopoDS_Shape>());
|
||||
wiresections[i].push_back(TopoDS::Wire(ex.Current()));
|
||||
}
|
||||
return i;
|
||||
};
|
||||
|
||||
double distanceFwd = tan(AngleFwd) * LengthFwd;
|
||||
double distanceRev = tan(AngleRev) * LengthRev;
|
||||
gp_Vec vecFwd = gp_Vec(direction) * LengthFwd;
|
||||
gp_Vec vecRev = gp_Vec(direction.Reversed()) * LengthRev;
|
||||
|
||||
bool bFwd = fabs(LengthFwd) > Precision::Confusion();
|
||||
bool bRev = fabs(LengthRev) > Precision::Confusion();
|
||||
// only if there is a 2nd direction and the negated angle is equal to the first one
|
||||
// we can omit the source shape as loft section
|
||||
bool bMid = !bFwd || !bRev || -1.0 * AngleFwd != AngleRev;
|
||||
|
||||
if (shape.IsNull())
|
||||
Standard_Failure::Raise("Not a valid shape");
|
||||
|
||||
// store all wires of the shape into an array
|
||||
size_t numWires = addWiresToWireSections(wiresections);
|
||||
if (numWires == 0)
|
||||
Standard_Failure::Raise("Extrusion: Input must not only consist if a vertex");
|
||||
|
||||
// to store the sections for the loft
|
||||
std::list<TopoDS_Wire> list_of_sections;
|
||||
|
||||
// we need for all found wires an offset copy of them
|
||||
// we store them in an array
|
||||
TopoDS_Wire offsetWire;
|
||||
std::vector<std::vector<TopoDS_Shape>> extrusionSections(wiresections.size(), std::vector<TopoDS_Shape>());
|
||||
size_t rows = 0;
|
||||
int numEdges = 0;
|
||||
|
||||
// We need to find out what are outer wires and what are inner ones
|
||||
// methods like checking the center of mass etc. don't help us here.
|
||||
// As solution we build a prism with every wire, then subtract every prism from each other.
|
||||
// If the moment of inertia changes by a subtraction, we have an inner wire prism.
|
||||
//
|
||||
// first build the prisms
|
||||
std::vector<TopoDS_Shape> resultPrisms;
|
||||
TopoDS_Shape singlePrism;
|
||||
for (auto& wireVector : wiresections) {
|
||||
for (auto& singleWire : wireVector) {
|
||||
BRepBuilderAPI_MakeFace mkFace(TopoDS::Wire(singleWire));
|
||||
auto tempFace = mkFace.Shape();
|
||||
BRepPrimAPI_MakePrism mkPrism(tempFace, vecFwd);
|
||||
if (!mkPrism.IsDone())
|
||||
Standard_Failure::Raise("Extrusion: Generating prism failed");
|
||||
singlePrism = mkPrism.Shape();
|
||||
resultPrisms.push_back(singlePrism);
|
||||
}
|
||||
}
|
||||
// create an array with false to store later which wires are inner ones
|
||||
std::vector<bool> isInnerWire(resultPrisms.size(), false);
|
||||
std::vector<bool> checklist(resultPrisms.size(), true);
|
||||
// finally check reecursively for inner wires
|
||||
checkInnerWires(isInnerWire, direction, checklist, false, resultPrisms);
|
||||
|
||||
// count the number of inner wires
|
||||
int numInnerWires = 0;
|
||||
for (auto isInner : isInnerWire) {
|
||||
if (isInner)
|
||||
++numInnerWires;
|
||||
}
|
||||
|
||||
// at first create offset wires for the reversed part of extrusion
|
||||
// it is important that these wires are the first loft section
|
||||
if (bRev) {
|
||||
// create an offset for all source wires
|
||||
rows = 0;
|
||||
for (auto& wireVector : wiresections) {
|
||||
for (auto& singleWire : wireVector) {
|
||||
// count number of edges
|
||||
numEdges = 0;
|
||||
TopExp_Explorer xp(singleWire, TopAbs_EDGE);
|
||||
while (xp.More()) {
|
||||
numEdges++;
|
||||
xp.Next();
|
||||
}
|
||||
// create an offset copy of the wire
|
||||
if (!isInnerWire[rows]) {
|
||||
// this is an outer wire
|
||||
createTaperedPrismOffset(TopoDS::Wire(singleWire), vecRev, distanceRev, numEdges, true, offsetWire);
|
||||
}
|
||||
else {
|
||||
// there is an OCC bug with single-edge wires (circles), see inside createTaperedPrismOffset
|
||||
if (numEdges > 1 || !isPartDesign)
|
||||
// inner wires must get the negated offset
|
||||
createTaperedPrismOffset(TopoDS::Wire(singleWire), vecRev, -distanceRev, numEdges, true, offsetWire);
|
||||
else
|
||||
// these wires must not get the negated offset
|
||||
createTaperedPrismOffset(TopoDS::Wire(singleWire), vecRev, distanceRev, numEdges, true, offsetWire);
|
||||
}
|
||||
if (offsetWire.IsNull())
|
||||
return;
|
||||
extrusionSections[rows].push_back(offsetWire);
|
||||
}
|
||||
++rows;
|
||||
}
|
||||
}
|
||||
|
||||
// add the source wire as middle section
|
||||
// it is important to add them after the reversed part
|
||||
if (bMid) {
|
||||
// transfer all source wires as they are to the array from which we build the shells
|
||||
rows = 0;
|
||||
for (auto& wireVector : wiresections) {
|
||||
for (auto& singleWire : wireVector) {
|
||||
extrusionSections[rows].push_back(singleWire);
|
||||
}
|
||||
rows++;
|
||||
}
|
||||
}
|
||||
|
||||
// finally add the forward extrusion offset wires
|
||||
// these wires must be the last loft section
|
||||
if (bFwd) {
|
||||
rows = 0;
|
||||
for (auto& wireVector : wiresections) {
|
||||
for (auto& singleWire : wireVector) {
|
||||
// count number of edges
|
||||
numEdges = 0;
|
||||
TopExp_Explorer xp(singleWire, TopAbs_EDGE);
|
||||
while (xp.More()) {
|
||||
numEdges++;
|
||||
xp.Next();
|
||||
}
|
||||
// create an offset copy of the wire
|
||||
if (!isInnerWire[rows]) {
|
||||
// this is an outer wire
|
||||
createTaperedPrismOffset(TopoDS::Wire(singleWire), vecFwd, distanceFwd, numEdges, false, offsetWire);
|
||||
}
|
||||
else {
|
||||
// there is an OCC bug with single-edge wires (circles), see inside createTaperedPrismOffset
|
||||
if (numEdges > 1 || !isPartDesign)
|
||||
// inner wires must get the negated offset
|
||||
createTaperedPrismOffset(TopoDS::Wire(singleWire), vecFwd, -distanceFwd, numEdges, false, offsetWire);
|
||||
else
|
||||
// these wires must not get the negated offset
|
||||
createTaperedPrismOffset(TopoDS::Wire(singleWire), vecFwd, distanceFwd, numEdges, false, offsetWire);
|
||||
}
|
||||
if (offsetWire.IsNull())
|
||||
return;
|
||||
extrusionSections[rows].push_back(offsetWire);
|
||||
}
|
||||
++rows;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// build all shells
|
||||
std::vector<TopoDS_Shape> shells;
|
||||
|
||||
for (auto& wires : extrusionSections) {
|
||||
BRepOffsetAPI_ThruSections mkTS(isSolid, /*ruled=*/Standard_True, Precision::Confusion());
|
||||
|
||||
for (auto& singleWire : wires) {
|
||||
if (singleWire.ShapeType() == TopAbs_VERTEX)
|
||||
mkTS.AddVertex(TopoDS::Vertex(singleWire));
|
||||
else
|
||||
mkTS.AddWire(TopoDS::Wire(singleWire));
|
||||
}
|
||||
mkTS.Build();
|
||||
if (!mkTS.IsDone())
|
||||
Standard_Failure::Raise("Extrusion: Loft could not be built");
|
||||
|
||||
shells.push_back(mkTS.Shape());
|
||||
}
|
||||
|
||||
if (isSolid) {
|
||||
// we only need to cut if we have inner wires
|
||||
if (numInnerWires > 0) {
|
||||
// we take every outer wire prism and cut subsequently all inner wires prisms from it
|
||||
// every resulting shape is the final drafted extrusion shape
|
||||
GProp_GProps tempProperties;
|
||||
Standard_Real momentOfInertiaInitial;
|
||||
Standard_Real momentOfInertiaFinal;
|
||||
std::vector<bool>::iterator isInnerWireIterator = isInnerWire.begin();
|
||||
std::vector<bool>::iterator isInnerWireIteratorLoop;
|
||||
for (auto itOuter = shells.begin(); itOuter != shells.end(); ++itOuter) {
|
||||
if (*isInnerWireIterator == true) {
|
||||
++isInnerWireIterator;
|
||||
continue;
|
||||
}
|
||||
isInnerWireIteratorLoop = isInnerWire.begin();
|
||||
for (auto itInner = shells.begin(); itInner != shells.end(); ++itInner) {
|
||||
if (itOuter == itInner || *isInnerWireIteratorLoop == false) {
|
||||
++isInnerWireIteratorLoop;
|
||||
continue;
|
||||
}
|
||||
// get MomentOfInertia of first shape
|
||||
BRepGProp::VolumeProperties(*itOuter, tempProperties);
|
||||
momentOfInertiaInitial = tempProperties.MomentOfInertia(gp_Ax1(gp_Pnt(), direction));
|
||||
BRepAlgoAPI_Cut mkCut(*itOuter, *itInner);
|
||||
if (!mkCut.IsDone())
|
||||
Standard_Failure::Raise("Extrusion: Final cut out failed");
|
||||
BRepGProp::VolumeProperties(mkCut.Shape(), tempProperties);
|
||||
momentOfInertiaFinal = tempProperties.MomentOfInertia(gp_Ax1(gp_Pnt(), direction));
|
||||
// if the whole shape was cut away the resulting shape is not Null but its MomentOfInertia is 0.0
|
||||
// therefore we have a valid cut if the MomentOfInertia is not zero and changed
|
||||
if ((momentOfInertiaInitial != momentOfInertiaFinal)
|
||||
&& (momentOfInertiaFinal > Precision::Confusion())) {
|
||||
// immediately update the outer shape since more inner wire prism might cut it
|
||||
*itOuter = mkCut.Shape();
|
||||
}
|
||||
++isInnerWireIteratorLoop;
|
||||
}
|
||||
drafts.push_back(*itOuter);
|
||||
++isInnerWireIterator;
|
||||
}
|
||||
}
|
||||
else
|
||||
// we already have the results
|
||||
for (auto it = shells.begin(); it != shells.end(); ++it)
|
||||
drafts.push_back(*it);
|
||||
}
|
||||
else { // no solid
|
||||
BRepBuilderAPI_Sewing sewer;
|
||||
sewer.SetTolerance(Precision::Confusion());
|
||||
for (TopoDS_Shape& s : shells)
|
||||
sewer.Add(s);
|
||||
sewer.Perform();
|
||||
drafts.push_back(sewer.SewedShape());
|
||||
}
|
||||
}
|
||||
catch (Standard_Failure& e) {
|
||||
throw Base::RuntimeError(e.GetMessageString());
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
throw Base::RuntimeError(e.what());
|
||||
}
|
||||
catch (...) {
|
||||
throw Base::CADKernelError("Extrusion: A fatal error occurred when making the loft");
|
||||
}
|
||||
}
|
||||
|
||||
void ExtrusionHelper::checkInnerWires(std::vector<bool>& isInnerWire, const gp_Dir direction,
|
||||
std::vector<bool>&checklist, bool forInner, std::vector<TopoDS_Shape> prisms)
|
||||
{
|
||||
// store the number of wires to be checked
|
||||
size_t numCheckWiresInitial = 0;
|
||||
for (auto checks : checklist) {
|
||||
if (checks)
|
||||
++numCheckWiresInitial;
|
||||
}
|
||||
GProp_GProps tempProperties;
|
||||
Standard_Real momentOfInertiaInitial;
|
||||
Standard_Real momentOfInertiaFinal;
|
||||
size_t numCheckWires = 0;
|
||||
std::vector<bool>::iterator isInnerWireIterator = isInnerWire.begin();
|
||||
std::vector<bool>::iterator toCheckIterator = checklist.begin();
|
||||
// create an array with false used later to store what can be cancelled from the checklist
|
||||
std::vector<bool> toDisable(checklist.size(), false);
|
||||
int outer = -1;
|
||||
// we cut every prism to be checked from the other to be checked ones
|
||||
// if nothing happens, a prism can be cancelled from the checklist
|
||||
for (auto itOuter = prisms.begin(); itOuter != prisms.end(); ++itOuter) {
|
||||
++outer;
|
||||
if (*toCheckIterator == false) {
|
||||
++isInnerWireIterator;
|
||||
++toCheckIterator;
|
||||
continue;
|
||||
}
|
||||
auto toCheckIteratorInner = checklist.begin();
|
||||
bool saveIsInnerWireIterator = *isInnerWireIterator;
|
||||
for (auto itInner = prisms.begin(); itInner != prisms.end(); ++itInner) {
|
||||
if (itOuter == itInner || *toCheckIteratorInner == false) {
|
||||
++toCheckIteratorInner;
|
||||
continue;
|
||||
}
|
||||
// get MomentOfInertia of first shape
|
||||
BRepGProp::VolumeProperties(*itInner, tempProperties);
|
||||
momentOfInertiaInitial = tempProperties.MomentOfInertia(gp_Ax1(gp_Pnt(), direction));
|
||||
BRepAlgoAPI_Cut mkCut(*itInner, *itOuter);
|
||||
if (!mkCut.IsDone())
|
||||
Standard_Failure::Raise("Extrusion: Cut out failed");
|
||||
BRepGProp::VolumeProperties(mkCut.Shape(), tempProperties);
|
||||
momentOfInertiaFinal = tempProperties.MomentOfInertia(gp_Ax1(gp_Pnt(), direction));
|
||||
// if the whole shape was cut away the resulting shape is not Null but its MomentOfInertia is 0.0
|
||||
// therefore we have an inner wire if the MomentOfInertia is not zero and changed
|
||||
if ((momentOfInertiaInitial != momentOfInertiaFinal)
|
||||
&& (momentOfInertiaFinal > Precision::Confusion())) {
|
||||
*isInnerWireIterator = !forInner;
|
||||
++numCheckWires;
|
||||
*toCheckIterator = true;
|
||||
break;
|
||||
}
|
||||
++toCheckIteratorInner;
|
||||
}
|
||||
if (saveIsInnerWireIterator == *isInnerWireIterator)
|
||||
// nothing was changed and we can remove it from the list to be checked
|
||||
// but we cannot do this before the foor loop was fully run
|
||||
toDisable[outer] = true;
|
||||
++isInnerWireIterator;
|
||||
++toCheckIterator;
|
||||
}
|
||||
|
||||
// cancel prisms from the checklist whose wire state did not change
|
||||
size_t i = 0;
|
||||
for (auto disable : toDisable) {
|
||||
if (disable)
|
||||
checklist[i] = false;
|
||||
++i;
|
||||
}
|
||||
|
||||
// if all wires are inner ones, we take the first one as outer and issue a warning
|
||||
if (numCheckWires == isInnerWire.size()) {
|
||||
isInnerWire[0] = false;
|
||||
checklist[0] = false;
|
||||
--numCheckWires;
|
||||
Base::Console().Warning("Extrusion: could not determine what structure is the outer one.\n\
|
||||
The first input one will now be taken as outer one.\n");
|
||||
}
|
||||
|
||||
// There can be cases with several wires all intersecting each other.
|
||||
// Then it is impossible to find out what wire is an inner one
|
||||
// and we can only treat all wires in the checklist as outer ones.
|
||||
if (numCheckWiresInitial == numCheckWires) {
|
||||
i = 0;
|
||||
for (auto checks : checklist) {
|
||||
if (checks) {
|
||||
isInnerWire[i] = false;
|
||||
checklist[i] = false;
|
||||
--numCheckWires;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
Base::Console().Warning("Extrusion: too many self-intersection structures!\n\
|
||||
Impossible to determine what structure is an inner one.\n\
|
||||
All undeterminable structures will therefore be taken as outer ones.\n");
|
||||
}
|
||||
|
||||
// recursively call the function until all wires are checked
|
||||
if (numCheckWires > 1)
|
||||
checkInnerWires(isInnerWire, direction, checklist, !forInner, prisms);
|
||||
};
|
||||
|
||||
void ExtrusionHelper::createTaperedPrismOffset(TopoDS_Wire sourceWire,
|
||||
const gp_Vec& translation,
|
||||
double offset,
|
||||
int numEdges,
|
||||
bool isSecond,
|
||||
TopoDS_Wire& result) {
|
||||
|
||||
// if the wire consists of a single edge which has applied a placement
|
||||
// then this placement must be reset because otherwise
|
||||
// BRepOffsetAPI_MakeOffset shows weird behaviour by applying the placement, see
|
||||
// https://dev.opencascade.org/content/brepoffsetapimakeoffset-wire-and-face-odd-occt-740
|
||||
gp_Trsf tempTransform;
|
||||
tempTransform.SetTranslation(translation);
|
||||
TopLoc_Location loc(tempTransform);
|
||||
TopoDS_Wire movedSourceWire = TopoDS::Wire(sourceWire.Moved(loc));
|
||||
|
||||
TopoDS_Shape offsetShape;
|
||||
if (fabs(offset) > Precision::Confusion()) {
|
||||
TopLoc_Location edgeLocation;
|
||||
if (numEdges == 1) {
|
||||
// create a new wire from the input wire to determine its location
|
||||
// to reset the location after the offet operation
|
||||
BRepBuilderAPI_MakeWire mkWire;
|
||||
TopExp_Explorer xp(sourceWire, TopAbs_EDGE);
|
||||
while (xp.More()) {
|
||||
TopoDS_Edge edge = TopoDS::Edge(xp.Current());
|
||||
edgeLocation = edge.Location();
|
||||
edge.Location(TopLoc_Location());
|
||||
mkWire.Add(edge);
|
||||
xp.Next();
|
||||
}
|
||||
movedSourceWire = mkWire.Wire();
|
||||
}
|
||||
// create the offset shape
|
||||
BRepOffsetAPI_MakeOffset mkOffset;
|
||||
mkOffset.Init(GeomAbs_Arc);
|
||||
mkOffset.Init(GeomAbs_Intersection);
|
||||
mkOffset.AddWire(movedSourceWire);
|
||||
try {
|
||||
mkOffset.Perform(offset);
|
||||
offsetShape = mkOffset.Shape();
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
throw Base::RuntimeError(e.what());
|
||||
result = TopoDS_Wire();
|
||||
}
|
||||
if (!mkOffset.IsDone()) {
|
||||
Standard_Failure::Raise("Extrusion: Offset could not be created");
|
||||
result = TopoDS_Wire();
|
||||
}
|
||||
if (numEdges == 1) {
|
||||
// we need to move the offset wire first back to its original position
|
||||
offsetShape.Move(edgeLocation);
|
||||
// now apply the translation
|
||||
offsetShape = offsetShape.Moved(loc);
|
||||
}
|
||||
}
|
||||
else {
|
||||
offsetShape = movedSourceWire;
|
||||
}
|
||||
if (offsetShape.IsNull()) {
|
||||
if (isSecond)
|
||||
Base::Console().Error("Extrusion: end face of tapered against extrusion is empty\n" \
|
||||
"This means most probably that the against taper angle is too large or small.\n");
|
||||
else
|
||||
Base::Console().Error("Extrusion: end face of tapered along extrusion is empty\n" \
|
||||
"This means most probably that the along taper angle is too large or small.\n");
|
||||
Standard_Failure::Raise("Extrusion: end face of tapered extrusion is empty");
|
||||
}
|
||||
// assure we return a wire and no edge
|
||||
TopAbs_ShapeEnum type = offsetShape.ShapeType();
|
||||
if (type == TopAbs_WIRE) {
|
||||
result = TopoDS::Wire(offsetShape);
|
||||
}
|
||||
else if (type == TopAbs_EDGE) {
|
||||
BRepBuilderAPI_MakeWire mkWire2(TopoDS::Edge(offsetShape));
|
||||
result = mkWire2.Wire();
|
||||
}
|
||||
else {
|
||||
// this happens usually if type == TopAbs_COMPOUND and means the angle is too small
|
||||
// since this is a common mistake users will quickly do, issue a warning dialog
|
||||
// FIXME: Standard_Failure::Raise or App::DocumentObjectExecReturn don't output the message to the user
|
||||
result = TopoDS_Wire();
|
||||
if (isSecond)
|
||||
Base::Console().Error("Extrusion: type of against extrusion end face is not supported.\n" \
|
||||
"This means most probably that the against taper angle is too large or small.\n");
|
||||
else
|
||||
Base::Console().Error("Extrusion: type of along extrusion is not supported.\n" \
|
||||
"This means most probably that the along taper angle is too large or small.\n");
|
||||
}
|
||||
|
||||
}
|
||||
73
src/Mod/Part/App/ExtrusionHelper.h
Normal file
73
src/Mod/Part/App/ExtrusionHelper.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2022 Uwe Stöhr <uwestoehr@lyx.org> *
|
||||
* *
|
||||
* 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 *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef PART_EXTRUSIONHELPER_H
|
||||
#define PART_EXTRUSIONHELPER_H
|
||||
|
||||
#include <list>
|
||||
#include <gp_Dir.hxx>
|
||||
#include <TopoDS_Shape.hxx>
|
||||
|
||||
namespace Part
|
||||
{
|
||||
|
||||
class PartExport ExtrusionHelper
|
||||
{
|
||||
public:
|
||||
ExtrusionHelper();
|
||||
|
||||
/**
|
||||
* @brief makeDraft: creates a drafted extrusion shape out of the input 2D shape
|
||||
*/
|
||||
static void makeDraft(const TopoDS_Shape& shape,
|
||||
const gp_Dir& direction,
|
||||
const double LengthFwd,
|
||||
const double LengthRev,
|
||||
const double AngleFwd,
|
||||
const double AngleRev,
|
||||
bool isSolid,
|
||||
std::list<TopoDS_Shape>& drafts,
|
||||
bool isPartDesign);
|
||||
/**
|
||||
* @brief checkInnerWires: Checks what wires are inner ones by taking a set of prisms created with every wire.
|
||||
* The prisms are cut from each other. If the moment of inertia thereby changes, the prism wire is an inner wire.
|
||||
* Inner wires can have nested inner wires that are then in fact outer wires.
|
||||
* Therefore checkInnerWires is called recursively until all wires are checked.
|
||||
*/
|
||||
static void checkInnerWires(std::vector<bool>& isInnerWire, const gp_Dir direction,
|
||||
std::vector<bool>& checklist, bool forInner, std::vector<TopoDS_Shape> prisms);
|
||||
/**
|
||||
* @brief createTaperedPrismOffset: creates an offset wire from the sourceWire in the specified
|
||||
* translation. isSecond determines if the wire is used for the 2nd extrusion direction.
|
||||
*/
|
||||
static void createTaperedPrismOffset(TopoDS_Wire sourceWire,
|
||||
const gp_Vec& translation,
|
||||
double offset,
|
||||
int numEdges,
|
||||
bool isSecond,
|
||||
TopoDS_Wire& result);
|
||||
};
|
||||
|
||||
} //namespace Part
|
||||
|
||||
#endif // PART_EXTRUSIONHELPER_H
|
||||
@@ -28,19 +28,12 @@
|
||||
# include <BRepAdaptor_Curve.hxx>
|
||||
# include <BRepAlgoAPI_Cut.hxx>
|
||||
# include <BRepBuilderAPI_Copy.hxx>
|
||||
# include <BRepBuilderAPI_MakeFace.hxx>
|
||||
# include <BRepBuilderAPI_MakeSolid.hxx>
|
||||
# include <BRepBuilderAPI_MakeWire.hxx>
|
||||
# include <BRepBuilderAPI_Sewing.hxx>
|
||||
# include <BRepClass3d_SolidClassifier.hxx>
|
||||
# include <BRepGProp.hxx>
|
||||
# include <BRepLib_FindSurface.hxx>
|
||||
# include <BRepOffsetAPI_MakeOffset.hxx>
|
||||
# include <BRepOffsetAPI_ThruSections.hxx>
|
||||
# include <BRepPrimAPI_MakePrism.hxx>
|
||||
# include <gp_Pln.hxx>
|
||||
# include <gp_Trsf.hxx>
|
||||
# include <GProp_GProps.hxx>
|
||||
# include <Precision.hxx>
|
||||
# include <ShapeAnalysis.hxx>
|
||||
# include <ShapeFix_Wire.hxx>
|
||||
@@ -55,6 +48,7 @@
|
||||
#include <App/Application.h>
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/Tools.h>
|
||||
#include "ExtrusionHelper.h"
|
||||
#include "Part2DObject.h"
|
||||
|
||||
using namespace Part;
|
||||
@@ -260,7 +254,9 @@ TopoShape Extrusion::extrudeShape(const TopoShape& source, const Extrusion::Extr
|
||||
myShape = BRepBuilderAPI_Copy(myShape).Shape();
|
||||
|
||||
std::list<TopoDS_Shape> drafts;
|
||||
makeDraft(params, myShape, drafts);
|
||||
bool isPartDesign = false; // there is an OCC bug with single-edge wires (circles) we need to treat differently for PD and Part
|
||||
ExtrusionHelper::makeDraft(myShape, params.dir, params.lengthFwd, params.lengthRev,
|
||||
params.taperAngleFwd, params.taperAngleRev, params.solid, drafts, isPartDesign);
|
||||
if (drafts.empty()) {
|
||||
Standard_Failure::Raise("Drafting shape failed");
|
||||
}
|
||||
@@ -339,434 +335,6 @@ App::DocumentObjectExecReturn* Extrusion::execute(void)
|
||||
}
|
||||
}
|
||||
|
||||
void Extrusion::makeDraft(const ExtrusionParameters& params, const TopoDS_Shape& shape, std::list<TopoDS_Shape>& drafts)
|
||||
{
|
||||
std::vector<std::vector<TopoDS_Shape>> wiresections;
|
||||
|
||||
auto addWiresToWireSections =
|
||||
[&shape](std::vector<std::vector<TopoDS_Shape>>& wiresections) -> size_t {
|
||||
TopExp_Explorer ex;
|
||||
size_t i = 0;
|
||||
for (ex.Init(shape, TopAbs_WIRE); ex.More(); ex.Next(), ++i) {
|
||||
wiresections.push_back(std::vector<TopoDS_Shape>());
|
||||
wiresections[i].push_back(TopoDS::Wire(ex.Current()));
|
||||
}
|
||||
return i;
|
||||
};
|
||||
|
||||
double distanceFwd = tan(params.taperAngleFwd)*params.lengthFwd;
|
||||
double distanceRev = tan(params.taperAngleRev)*params.lengthRev;
|
||||
gp_Vec vecFwd = gp_Vec(params.dir) * params.lengthFwd;
|
||||
gp_Vec vecRev = gp_Vec(params.dir.Reversed()) * params.lengthRev;
|
||||
|
||||
bool bFwd = fabs(params.lengthFwd) > Precision::Confusion();
|
||||
bool bRev = fabs(params.lengthRev) > Precision::Confusion();
|
||||
// only if there is a 2nd direction and the negated angle is equal to the first one
|
||||
// we can omit the source shape as loft section
|
||||
bool bMid = !bFwd || !bRev || -1.0 * params.taperAngleFwd != params.taperAngleRev;
|
||||
|
||||
if (shape.IsNull())
|
||||
Standard_Failure::Raise("Not a valid shape");
|
||||
|
||||
// store all wires of the shape into an array
|
||||
size_t numWires = addWiresToWireSections(wiresections);
|
||||
if (numWires == 0)
|
||||
Standard_Failure::Raise("Extrusion: Input must not only consist if a vertex");
|
||||
|
||||
// to store the sections for the loft
|
||||
std::list<TopoDS_Wire> list_of_sections;
|
||||
|
||||
// we need for all found wires an offset copy of them
|
||||
// we store them in an array
|
||||
TopoDS_Wire offsetWire;
|
||||
std::vector<std::vector<TopoDS_Shape>> extrusionSections(wiresections.size(), std::vector<TopoDS_Shape>());
|
||||
size_t rows = 0;
|
||||
int numEdges = 0;
|
||||
|
||||
// We need to find out what are outer wires and what are inner ones
|
||||
// methods like checking the center of mass etc. don't help us here.
|
||||
// As solution we build a prism with every wire, then subtract every prism from each other.
|
||||
// If the moment of inertia changes by a subtraction, we have an inner wire prism.
|
||||
//
|
||||
// first build the prisms
|
||||
std::vector<TopoDS_Shape> resultPrisms;
|
||||
TopoDS_Shape singlePrism;
|
||||
for (auto& wireVector : wiresections) {
|
||||
for (auto& singleWire : wireVector) {
|
||||
BRepBuilderAPI_MakeFace mkFace(TopoDS::Wire(singleWire));
|
||||
auto tempFace = mkFace.Shape();
|
||||
BRepPrimAPI_MakePrism mkPrism(tempFace, vecFwd);
|
||||
if(!mkPrism.IsDone())
|
||||
Standard_Failure::Raise("Extrusion: Generating prism failed");
|
||||
singlePrism = mkPrism.Shape();
|
||||
resultPrisms.push_back(singlePrism);
|
||||
}
|
||||
}
|
||||
// create an array with false to store later which wires are inner ones
|
||||
std::vector<bool> isInnerWire(resultPrisms.size(), false);
|
||||
std::vector<bool> checklist(resultPrisms.size(), true);
|
||||
// finally check reecursively for inner wires
|
||||
checkInnerWires(isInnerWire, params.dir, checklist, false, resultPrisms);
|
||||
|
||||
// count the number of inner wires
|
||||
int numInnerWires = 0;
|
||||
for (auto isInner : isInnerWire) {
|
||||
if (isInner)
|
||||
++numInnerWires;
|
||||
}
|
||||
|
||||
// at first create offset wires for the reversed part of extrusion
|
||||
// it is important that these wires are the first loft section
|
||||
if (bRev) {
|
||||
// create an offset for all source wires
|
||||
rows = 0;
|
||||
for (auto& wireVector : wiresections) {
|
||||
for (auto& singleWire : wireVector) {
|
||||
// count number of edges
|
||||
numEdges = 0;
|
||||
TopExp_Explorer xp(singleWire, TopAbs_EDGE);
|
||||
while (xp.More()) {
|
||||
numEdges++;
|
||||
xp.Next();
|
||||
}
|
||||
// create an offset copy of the wire
|
||||
if (!isInnerWire[rows]) {
|
||||
// this is an outer wire
|
||||
createTaperedPrismOffset(TopoDS::Wire(singleWire), vecRev, distanceRev, numEdges, true, offsetWire);
|
||||
}
|
||||
else {
|
||||
// inner wires must be reversed and get the negated offset
|
||||
createTaperedPrismOffset(TopoDS::Wire(singleWire.Reversed()), vecRev, -distanceRev, numEdges, true, offsetWire);
|
||||
}
|
||||
if (offsetWire.IsNull())
|
||||
return;
|
||||
extrusionSections[rows].push_back(offsetWire);
|
||||
}
|
||||
++rows;
|
||||
}
|
||||
}
|
||||
|
||||
// add the source wire as middle section
|
||||
// it is important to add them after the reversed part
|
||||
if (bMid) {
|
||||
// transfer all source wires as they are to the array from which we build the shells
|
||||
rows = 0;
|
||||
for (auto& wireVector : wiresections) {
|
||||
for (auto& singleWire : wireVector) {
|
||||
extrusionSections[rows].push_back(singleWire);
|
||||
}
|
||||
rows++;
|
||||
}
|
||||
}
|
||||
|
||||
// finally add the forward extrusion offset wires
|
||||
// these wires must be the last loft section
|
||||
if (bFwd) {
|
||||
rows = 0;
|
||||
for (auto& wireVector : wiresections) {
|
||||
for (auto& singleWire : wireVector) {
|
||||
// count number of edges
|
||||
numEdges = 0;
|
||||
TopExp_Explorer xp(singleWire, TopAbs_EDGE);
|
||||
while (xp.More()) {
|
||||
numEdges++;
|
||||
xp.Next();
|
||||
}
|
||||
// create an offset copy of the wire
|
||||
if (!isInnerWire[rows]) {
|
||||
// this is an outer wire
|
||||
createTaperedPrismOffset(TopoDS::Wire(singleWire), vecFwd, distanceFwd, numEdges, false, offsetWire);
|
||||
}
|
||||
else {
|
||||
// inner wires must be reversed and get the negated offset
|
||||
createTaperedPrismOffset(TopoDS::Wire(singleWire.Reversed()), vecFwd, -distanceFwd, numEdges, false, offsetWire);
|
||||
}
|
||||
if (offsetWire.IsNull())
|
||||
return;
|
||||
extrusionSections[rows].push_back(offsetWire);
|
||||
}
|
||||
++rows;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// build all shells
|
||||
std::vector<TopoDS_Shape> shells;
|
||||
|
||||
for (auto& wires : extrusionSections) {
|
||||
BRepOffsetAPI_ThruSections mkTS(params.solid, /*ruled=*/Standard_True, Precision::Confusion());
|
||||
|
||||
for (auto& singleWire : wires) {
|
||||
if (singleWire.ShapeType() == TopAbs_VERTEX)
|
||||
mkTS.AddVertex(TopoDS::Vertex(singleWire));
|
||||
else
|
||||
mkTS.AddWire(TopoDS::Wire(singleWire));
|
||||
}
|
||||
mkTS.Build();
|
||||
if (!mkTS.IsDone())
|
||||
Standard_Failure::Raise("Extrusion: Loft could not be built");
|
||||
|
||||
shells.push_back(mkTS.Shape());
|
||||
}
|
||||
|
||||
if (params.solid) {
|
||||
// we only need to cut if we have inner wires
|
||||
if (numInnerWires > 0) {
|
||||
// we take every outer wire prism and cut subsequently all inner wires prisms from it
|
||||
// every resulting shape is the final drafted extrusion shape
|
||||
GProp_GProps tempProperties;
|
||||
Standard_Real momentOfInertiaInitial;
|
||||
Standard_Real momentOfInertiaFinal;
|
||||
std::vector<bool>::iterator isInnerWireIterator = isInnerWire.begin();
|
||||
std::vector<bool>::iterator isInnerWireIteratorLoop;
|
||||
for (auto itOuter = shells.begin(); itOuter != shells.end(); ++itOuter) {
|
||||
if (*isInnerWireIterator == true) {
|
||||
++isInnerWireIterator;
|
||||
continue;
|
||||
}
|
||||
isInnerWireIteratorLoop = isInnerWire.begin();
|
||||
for (auto itInner = shells.begin(); itInner != shells.end(); ++itInner) {
|
||||
if (itOuter == itInner || *isInnerWireIteratorLoop == false) {
|
||||
++isInnerWireIteratorLoop;
|
||||
continue;
|
||||
}
|
||||
// get MomentOfInertia of first shape
|
||||
BRepGProp::VolumeProperties(*itOuter, tempProperties);
|
||||
momentOfInertiaInitial = tempProperties.MomentOfInertia(gp_Ax1(gp_Pnt(), params.dir));
|
||||
BRepAlgoAPI_Cut mkCut(*itOuter, *itInner);
|
||||
if (!mkCut.IsDone())
|
||||
Standard_Failure::Raise("Extrusion: Final cut out failed");
|
||||
BRepGProp::VolumeProperties(mkCut.Shape(), tempProperties);
|
||||
momentOfInertiaFinal = tempProperties.MomentOfInertia(gp_Ax1(gp_Pnt(), params.dir));
|
||||
// if the whole shape was cut away the resulting shape is not Null but its MomentOfInertia is 0.0
|
||||
// therefore we have a valid cut if the MomentOfInertia is not zero and changed
|
||||
if ((momentOfInertiaInitial != momentOfInertiaFinal)
|
||||
&& (momentOfInertiaFinal > Precision::Confusion())) {
|
||||
// immediately update the outer shape since more inner wire prism might cut it
|
||||
*itOuter = mkCut.Shape();
|
||||
}
|
||||
++isInnerWireIteratorLoop;
|
||||
}
|
||||
drafts.push_back(*itOuter);
|
||||
++isInnerWireIterator;
|
||||
}
|
||||
} else
|
||||
// we already have the results
|
||||
for (auto it = shells.begin(); it != shells.end(); ++it)
|
||||
drafts.push_back(*it);
|
||||
}
|
||||
else { // no solid
|
||||
BRepBuilderAPI_Sewing sewer;
|
||||
sewer.SetTolerance(Precision::Confusion());
|
||||
for (TopoDS_Shape& s : shells)
|
||||
sewer.Add(s);
|
||||
sewer.Perform();
|
||||
drafts.push_back(sewer.SewedShape());
|
||||
}
|
||||
}
|
||||
catch (Standard_Failure& e) {
|
||||
throw Base::RuntimeError(e.GetMessageString());
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
throw Base::RuntimeError(e.what());
|
||||
}
|
||||
catch (...) {
|
||||
throw Base::CADKernelError("Extrusion: A fatal error occurred when making the loft");
|
||||
}
|
||||
}
|
||||
|
||||
void Extrusion::checkInnerWires(std::vector<bool>& isInnerWire, const gp_Dir direction,
|
||||
std::vector<bool>& checklist, bool forInner, std::vector<TopoDS_Shape> prisms)
|
||||
{
|
||||
// store the number of wires to be checked
|
||||
size_t numCheckWiresInitial = 0;
|
||||
for (auto checks : checklist) {
|
||||
if (checks)
|
||||
++numCheckWiresInitial;
|
||||
}
|
||||
GProp_GProps tempProperties;
|
||||
Standard_Real momentOfInertiaInitial;
|
||||
Standard_Real momentOfInertiaFinal;
|
||||
size_t numCheckWires = 0;
|
||||
std::vector<bool>::iterator isInnerWireIterator = isInnerWire.begin();
|
||||
std::vector<bool>::iterator toCheckIterator = checklist.begin();
|
||||
// create an array with false used later to store what can be cancelled from the checklist
|
||||
std::vector<bool> toDisable(checklist.size(), false);
|
||||
int outer = -1;
|
||||
// we cut every prism to be checked from the other to be checked ones
|
||||
// if nothing happens, a prism can be cancelled from the checklist
|
||||
for (auto itOuter = prisms.begin(); itOuter != prisms.end(); ++itOuter) {
|
||||
++outer;
|
||||
if (*toCheckIterator == false) {
|
||||
++isInnerWireIterator;
|
||||
++toCheckIterator;
|
||||
continue;
|
||||
}
|
||||
auto toCheckIteratorInner = checklist.begin();
|
||||
bool saveIsInnerWireIterator = *isInnerWireIterator;
|
||||
for (auto itInner = prisms.begin(); itInner != prisms.end(); ++itInner) {
|
||||
if (itOuter == itInner || *toCheckIteratorInner == false) {
|
||||
++toCheckIteratorInner;
|
||||
continue;
|
||||
}
|
||||
// get MomentOfInertia of first shape
|
||||
BRepGProp::VolumeProperties(*itInner, tempProperties);
|
||||
momentOfInertiaInitial = tempProperties.MomentOfInertia(gp_Ax1(gp_Pnt(), direction));
|
||||
BRepAlgoAPI_Cut mkCut(*itInner, *itOuter);
|
||||
if (!mkCut.IsDone())
|
||||
Standard_Failure::Raise("Extrusion: Cut out failed");
|
||||
BRepGProp::VolumeProperties(mkCut.Shape(), tempProperties);
|
||||
momentOfInertiaFinal = tempProperties.MomentOfInertia(gp_Ax1(gp_Pnt(), direction));
|
||||
// if the whole shape was cut away the resulting shape is not Null but its MomentOfInertia is 0.0
|
||||
// therefore we have an inner wire if the MomentOfInertia is not zero and changed
|
||||
if ((momentOfInertiaInitial != momentOfInertiaFinal)
|
||||
&& (momentOfInertiaFinal > Precision::Confusion())) {
|
||||
*isInnerWireIterator = !forInner;
|
||||
++numCheckWires;
|
||||
*toCheckIterator = true;
|
||||
break;
|
||||
}
|
||||
++toCheckIteratorInner;
|
||||
}
|
||||
if (saveIsInnerWireIterator == *isInnerWireIterator)
|
||||
// nothing was changed and we can remove it from the list to be checked
|
||||
// but we cannot do this before the foor loop was fully run
|
||||
toDisable[outer] = true;
|
||||
++isInnerWireIterator;
|
||||
++toCheckIterator;
|
||||
}
|
||||
|
||||
// cancel prisms from the checklist whose wire state did not change
|
||||
size_t i = 0;
|
||||
for (auto disable : toDisable) {
|
||||
if (disable)
|
||||
checklist[i] = false;
|
||||
++i;
|
||||
}
|
||||
|
||||
// if all wires are inner ones, we take the first one as outer and issue a warning
|
||||
if (numCheckWires == isInnerWire.size()) {
|
||||
isInnerWire[0] = false;
|
||||
checklist[0] = false;
|
||||
--numCheckWires;
|
||||
Base::Console().Warning("Extrusion: could not determine what structure is the outer one.\n\
|
||||
The first input one will now be taken as outer one.\n");
|
||||
}
|
||||
|
||||
// There can be cases with several wires all intersecting each other.
|
||||
// Then it is impossible to find out what wire is an inner one
|
||||
// and we can only treat all wires in the checklist as outer ones.
|
||||
if (numCheckWiresInitial == numCheckWires) {
|
||||
i = 0;
|
||||
for (auto checks : checklist) {
|
||||
if (checks) {
|
||||
isInnerWire[i] = false;
|
||||
checklist[i] = false;
|
||||
--numCheckWires;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
Base::Console().Warning("Extrusion: too many self-intersection structures!\n\
|
||||
Impossible to determine what structure is an inner one.\n\
|
||||
All undeterminable structures will therefore be taken as outer ones.\n");
|
||||
}
|
||||
|
||||
// recursively call the function until all wires are checked
|
||||
if (numCheckWires > 1)
|
||||
checkInnerWires(isInnerWire, direction, checklist, !forInner, prisms);
|
||||
};
|
||||
|
||||
void Extrusion::createTaperedPrismOffset(TopoDS_Wire sourceWire,
|
||||
const gp_Vec& translation,
|
||||
double offset,
|
||||
int numEdges,
|
||||
bool isSecond,
|
||||
TopoDS_Wire& result) {
|
||||
|
||||
// if the wire consists of a single edge which has applied a placement
|
||||
// then this placement must be reset because otherwise
|
||||
// BRepOffsetAPI_MakeOffset shows weird behaviour by applying the placement
|
||||
gp_Trsf tempTransform;
|
||||
tempTransform.SetTranslation(translation);
|
||||
TopLoc_Location loc(tempTransform);
|
||||
TopoDS_Wire movedSourceWire = TopoDS::Wire(sourceWire.Moved(loc));
|
||||
|
||||
TopoDS_Shape offsetShape;
|
||||
if (fabs(offset) > Precision::Confusion()) {
|
||||
TopLoc_Location edgeLocation;
|
||||
if (numEdges == 1) {
|
||||
// create a new wire from the input wire to determine its location
|
||||
// to reset the location after the offet operation
|
||||
BRepBuilderAPI_MakeWire mkWire;
|
||||
TopExp_Explorer xp(sourceWire, TopAbs_EDGE);
|
||||
while (xp.More()) {
|
||||
TopoDS_Edge edge = TopoDS::Edge(xp.Current());
|
||||
edgeLocation = edge.Location();
|
||||
edge.Location(TopLoc_Location());
|
||||
mkWire.Add(edge);
|
||||
xp.Next();
|
||||
}
|
||||
movedSourceWire = mkWire.Wire();
|
||||
}
|
||||
// create the offset shape
|
||||
BRepOffsetAPI_MakeOffset mkOffset;
|
||||
mkOffset.Init(GeomAbs_Arc);
|
||||
mkOffset.Init(GeomAbs_Intersection);
|
||||
mkOffset.AddWire(movedSourceWire);
|
||||
try {
|
||||
mkOffset.Perform(offset);
|
||||
offsetShape = mkOffset.Shape();
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
throw Base::RuntimeError(e.what());
|
||||
result = TopoDS_Wire();
|
||||
}
|
||||
if (!mkOffset.IsDone()) {
|
||||
Standard_Failure::Raise("Extrusion: Offset could not be created");
|
||||
result = TopoDS_Wire();
|
||||
}
|
||||
if (numEdges == 1) {
|
||||
// we need to move the offset wire first back to its original position
|
||||
offsetShape.Move(edgeLocation);
|
||||
// now apply the translation
|
||||
offsetShape = offsetShape.Moved(loc);
|
||||
}
|
||||
}
|
||||
else {
|
||||
offsetShape = movedSourceWire;
|
||||
}
|
||||
if (offsetShape.IsNull()) {
|
||||
if (isSecond)
|
||||
Base::Console().Error("Extrusion: end face of tapered against extrusion is empty\n" \
|
||||
"This means most probably that the against taper angle is too large or small.\n");
|
||||
else
|
||||
Base::Console().Error("Extrusion: end face of tapered along extrusion is empty\n" \
|
||||
"This means most probably that the along taper angle is too large or small.\n");
|
||||
Standard_Failure::Raise("Extrusion: end face of tapered extrusion is empty");
|
||||
}
|
||||
// assure we return a wire and no edge
|
||||
TopAbs_ShapeEnum type = offsetShape.ShapeType();
|
||||
if (type == TopAbs_WIRE) {
|
||||
result = TopoDS::Wire(offsetShape);
|
||||
}
|
||||
else if (type == TopAbs_EDGE) {
|
||||
BRepBuilderAPI_MakeWire mkWire2(TopoDS::Edge(offsetShape));
|
||||
result = mkWire2.Wire();
|
||||
}
|
||||
else {
|
||||
// this happens usually if type == TopAbs_COMPOUND and means the angle is too small
|
||||
// since this is a common mistake users will quickly do, issue a warning dialog
|
||||
// FIXME: Standard_Failure::Raise or App::DocumentObjectExecReturn don't output the message to the user
|
||||
result = TopoDS_Wire();
|
||||
if (isSecond)
|
||||
Base::Console().Error("Extrusion: type of against extrusion end face is not supported.\n" \
|
||||
"This means most probably that the against taper angle is too large or small.\n");
|
||||
else
|
||||
Base::Console().Error("Extrusion: type of along extrusion is not supported.\n" \
|
||||
"This means most probably that the along taper angle is too large or small.\n");
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
TYPESYSTEM_SOURCE(Part::FaceMakerExtrusion, Part::FaceMakerCheese)
|
||||
|
||||
@@ -120,29 +120,6 @@ public: //mode enumerations
|
||||
};
|
||||
static const char* eDirModeStrings[];
|
||||
|
||||
protected:
|
||||
static void makeDraft(const ExtrusionParameters& params, const TopoDS_Shape&, std::list<TopoDS_Shape>&);
|
||||
|
||||
/**
|
||||
* @brief checkInnerWires: Checks what wires are inner ones by taking a set of prisms created with every wire.
|
||||
* The prisms are cut from each other. If the moment of inertia thereby changes, the prism wire is an inner wire.
|
||||
* Inner wires can have nested inner wires that are then in fact outer wires.
|
||||
* Therefore checkInnerWires is called recursively until all wires are checked.
|
||||
*/
|
||||
static void checkInnerWires(std::vector<bool>& isInnerWire, const gp_Dir direction,
|
||||
std::vector<bool>& checklist, bool forInner, std::vector<TopoDS_Shape> prisms);
|
||||
|
||||
/**
|
||||
* @brief createTaperedPrismOffset: creates an offset wire from the sourceWire in the specified
|
||||
* translation. isSecond determines if the wire is used for the 2nd extrusion direction.
|
||||
*/
|
||||
static void createTaperedPrismOffset(TopoDS_Wire sourceWire,
|
||||
const gp_Vec& translation,
|
||||
double offset,
|
||||
int numEdges,
|
||||
bool isSecond,
|
||||
TopoDS_Wire& result);
|
||||
|
||||
protected:
|
||||
virtual void setupObject() override;
|
||||
};
|
||||
|
||||
@@ -51,15 +51,17 @@
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/Placement.h>
|
||||
#include <Base/Reader.h>
|
||||
#include <Base/Tools.h>
|
||||
|
||||
#include "FeatureExtrude.h"
|
||||
|
||||
using namespace PartDesign;
|
||||
|
||||
|
||||
PROPERTY_SOURCE(PartDesign::FeatureExtrude, PartDesign::ProfileBased)
|
||||
|
||||
App::PropertyQuantityConstraint::Constraints FeatureExtrude::signedLengthConstraint = { -DBL_MAX, DBL_MAX, 1.0 };
|
||||
double FeatureExtrude::maxAngle = 90 - Base::toDegrees<double>(Precision::Angular());
|
||||
App::PropertyAngle::Constraints FeatureExtrude::floatAngle = { -maxAngle, maxAngle, 1.0 };
|
||||
|
||||
FeatureExtrude::FeatureExtrude()
|
||||
{
|
||||
@@ -71,6 +73,8 @@ short FeatureExtrude::mustExecute() const
|
||||
Type.isTouched() ||
|
||||
Length.isTouched() ||
|
||||
Length2.isTouched() ||
|
||||
TaperAngle.isTouched() ||
|
||||
TaperAngle2.isTouched() ||
|
||||
UseCustomVector.isTouched() ||
|
||||
Direction.isTouched() ||
|
||||
ReferenceAxis.isTouched() ||
|
||||
|
||||
@@ -24,8 +24,10 @@
|
||||
#ifndef PARTDESIGN_FEATURE_EXTRUDE_H
|
||||
#define PARTDESIGN_FEATURE_EXTRUDE_H
|
||||
|
||||
#include <App/PropertyGeo.h>
|
||||
#include <App/PropertyStandard.h>
|
||||
#include <App/PropertyUnits.h>
|
||||
#include <Base/Tools.h>
|
||||
#include "FeatureSketchBased.h"
|
||||
|
||||
namespace PartDesign
|
||||
@@ -41,6 +43,8 @@ public:
|
||||
App::PropertyEnumeration Type;
|
||||
App::PropertyLength Length;
|
||||
App::PropertyLength Length2;
|
||||
App::PropertyAngle TaperAngle;
|
||||
App::PropertyAngle TaperAngle2;
|
||||
App::PropertyBool UseCustomVector;
|
||||
App::PropertyVector Direction;
|
||||
App::PropertyBool AlongSketchNormal;
|
||||
@@ -48,6 +52,8 @@ public:
|
||||
App::PropertyLinkSub ReferenceAxis;
|
||||
|
||||
static App::PropertyQuantityConstraint::Constraints signedLengthConstraint;
|
||||
static double maxAngle;
|
||||
static App::PropertyAngle::Constraints floatAngle;
|
||||
|
||||
/** @name methods override feature */
|
||||
//@{
|
||||
|
||||
@@ -75,6 +75,10 @@ Pad::Pad()
|
||||
ADD_PROPERTY_TYPE(UpToFace, (0), "Pad", App::Prop_None, "Face where pad will end");
|
||||
ADD_PROPERTY_TYPE(Offset, (0.0), "Pad", App::Prop_None, "Offset from face in which pad will end");
|
||||
Offset.setConstraints(&signedLengthConstraint);
|
||||
ADD_PROPERTY_TYPE(TaperAngle, (0.0), "Pad", App::Prop_None, "Taper angle");
|
||||
TaperAngle.setConstraints(&floatAngle);
|
||||
ADD_PROPERTY_TYPE(TaperAngle2, (0.0), "Pad", App::Prop_None, "Taper angle for 2nd direction");
|
||||
TaperAngle2.setConstraints(&floatAngle);
|
||||
|
||||
// Remove the constraints and keep the type to allow to accept negative values
|
||||
// https://forum.freecadweb.org/viewtopic.php?f=3&t=52075&p=448410#p447636
|
||||
@@ -186,7 +190,7 @@ App::DocumentObjectExecReturn *Pad::execute()
|
||||
supportface = TopoDS_Face();
|
||||
|
||||
PrismMode mode = PrismMode::None;
|
||||
generatePrism(prism, method, base, sketchshape, supportface, upToFace, dir, mode, Standard_True);
|
||||
Extrude(prism, method, base, sketchshape, supportface, upToFace, dir, mode, Standard_True);
|
||||
base.Nullify();
|
||||
}
|
||||
else {
|
||||
@@ -203,12 +207,12 @@ App::DocumentObjectExecReturn *Pad::execute()
|
||||
if (!Ex.More())
|
||||
supportface = TopoDS_Face();
|
||||
PrismMode mode = PrismMode::None;
|
||||
generatePrism(prism, method, base, sketchshape, supportface, upToFace, dir, mode, Standard_True);
|
||||
Extrude(prism, method, base, sketchshape, supportface, upToFace, dir, mode, Standard_True);
|
||||
}
|
||||
}
|
||||
else {
|
||||
generatePrism(prism, sketchshape, method, dir, L, L2,
|
||||
hasMidplane, hasReversed);
|
||||
Extrude(prism, sketchshape, method, dir, L, L2,
|
||||
TaperAngle.getValue(), TaperAngle2.getValue(), hasMidplane, hasReversed);
|
||||
}
|
||||
|
||||
if (prism.IsNull())
|
||||
|
||||
@@ -74,6 +74,10 @@ Pocket::Pocket()
|
||||
ADD_PROPERTY_TYPE(UpToFace, (0), "Pocket", App::Prop_None, "Face where pocket will end");
|
||||
ADD_PROPERTY_TYPE(Offset, (0.0), "Pocket", App::Prop_None, "Offset from face in which pocket will end");
|
||||
Offset.setConstraints(&signedLengthConstraint);
|
||||
ADD_PROPERTY_TYPE(TaperAngle, (0.0), "Pocket", App::Prop_None, "Taper angle");
|
||||
TaperAngle.setConstraints(&floatAngle);
|
||||
ADD_PROPERTY_TYPE(TaperAngle2, (0.0), "Pocket", App::Prop_None, "Taper angle for 2nd direction");
|
||||
TaperAngle2.setConstraints(&floatAngle);
|
||||
|
||||
// Remove the constraints and keep the type to allow to accept negative values
|
||||
// https://forum.freecadweb.org/viewtopic.php?f=3&t=52075&p=448410#p447636
|
||||
@@ -196,7 +200,7 @@ App::DocumentObjectExecReturn *Pocket::execute()
|
||||
supportface = TopoDS_Face();
|
||||
TopoDS_Shape prism;
|
||||
PrismMode mode = PrismMode::CutFromBase;
|
||||
generatePrism(prism, method, base, profileshape, supportface, upToFace, dir, mode, Standard_True);
|
||||
Extrude(prism, method, base, profileshape, supportface, upToFace, dir, mode, Standard_True);
|
||||
|
||||
// And the really expensive way to get the SubShape...
|
||||
BRepAlgoAPI_Cut mkCut(base, prism);
|
||||
@@ -215,8 +219,8 @@ App::DocumentObjectExecReturn *Pocket::execute()
|
||||
}
|
||||
else {
|
||||
TopoDS_Shape prism;
|
||||
generatePrism(prism, profileshape, method, dir, L, L2,
|
||||
Midplane.getValue(), Reversed.getValue());
|
||||
Extrude(prism, profileshape, method, dir, L, L2, TaperAngle.getValue(), TaperAngle2.getValue(),
|
||||
Midplane.getValue(), Reversed.getValue());
|
||||
|
||||
if (prism.IsNull())
|
||||
return new App::DocumentObjectExecReturn("Pocket: Resulting shape is empty");
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
# include <BRepBuilderAPI_Copy.hxx>
|
||||
# include <BRepBuilderAPI_MakeEdge.hxx>
|
||||
# include <BRepBuilderAPI_MakeFace.hxx>
|
||||
# include <BRepBuilderAPI_MakeWire.hxx>
|
||||
# include <BRepCheck_Analyzer.hxx>
|
||||
# include <BRepExtrema_DistShapeShape.hxx>
|
||||
# include <BRepGProp.hxx>
|
||||
@@ -42,6 +43,8 @@
|
||||
# include <BRepLProp_SLProps.hxx>
|
||||
# include <BRepPrimAPI_MakePrism.hxx>
|
||||
# include <BRepFeat_MakePrism.hxx>
|
||||
# include <BRepOffsetAPI_MakeOffset.hxx>
|
||||
# include <BRepOffsetAPI_ThruSections.hxx>
|
||||
# include <BRepProj_Projection.hxx>
|
||||
# include <Extrema_ExtCC.hxx>
|
||||
# include <Extrema_POnCurv.hxx>
|
||||
@@ -73,9 +76,11 @@
|
||||
#include <Base/Parameter.h>
|
||||
#include <Base/Reader.h>
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Tools.h>
|
||||
#include <App/Application.h>
|
||||
#include <App/OriginFeature.h>
|
||||
#include <App/Document.h>
|
||||
#include <Mod/Part/App/ExtrusionHelper.h>
|
||||
#include <Mod/Part/App/FaceMakerCheese.h>
|
||||
#include "FeatureSketchBased.h"
|
||||
#include "DatumPlane.h"
|
||||
@@ -580,18 +585,21 @@ double ProfileBased::getThroughAllLength() const
|
||||
return 2.02 * sqrt(box.SquareExtent());
|
||||
}
|
||||
|
||||
void ProfileBased::generatePrism(TopoDS_Shape& prism,
|
||||
const TopoDS_Shape& sketchshape,
|
||||
const std::string& method,
|
||||
const gp_Dir& dir,
|
||||
const double L,
|
||||
const double L2,
|
||||
const bool midplane,
|
||||
const bool reversed)
|
||||
void ProfileBased::Extrude(TopoDS_Shape& prism,
|
||||
const TopoDS_Shape& sketchshape,
|
||||
const std::string& method,
|
||||
const gp_Dir& direction,
|
||||
const double L,
|
||||
const double L2,
|
||||
const double angle,
|
||||
const double angle2,
|
||||
const bool midplane,
|
||||
const bool reversed)
|
||||
{
|
||||
if (method == "Length" || method == "TwoLengths" || method == "ThroughAll") {
|
||||
double Ltotal = L;
|
||||
double Loffset = 0.;
|
||||
gp_Dir directionTaper = direction;
|
||||
if (method == "ThroughAll")
|
||||
Ltotal = getThroughAllLength();
|
||||
|
||||
@@ -613,7 +621,7 @@ void ProfileBased::generatePrism(TopoDS_Shape& prism,
|
||||
TopoDS_Shape from = sketchshape;
|
||||
if (method == "TwoLengths" || midplane) {
|
||||
gp_Trsf mov;
|
||||
mov.SetTranslation(Loffset * gp_Vec(dir));
|
||||
mov.SetTranslation(Loffset * gp_Vec(direction));
|
||||
TopLoc_Location loc(mov);
|
||||
from = sketchshape.Moved(loc);
|
||||
}
|
||||
@@ -628,12 +636,25 @@ void ProfileBased::generatePrism(TopoDS_Shape& prism,
|
||||
throw Base::ValueError("Cannot create a pocket with a depth of zero.");
|
||||
}
|
||||
|
||||
// Its better not to use BRepFeat_MakePrism here even if we have a support because the
|
||||
// resulting shape creates problems with Pocket
|
||||
BRepPrimAPI_MakePrism PrismMaker(from, Ltotal * gp_Vec(dir), 0, 1); // finite prism
|
||||
if (!PrismMaker.IsDone())
|
||||
throw Base::RuntimeError("ProfileBased: Length: Could not extrude the sketch!");
|
||||
prism = PrismMaker.Shape();
|
||||
// now we can create either a tapered or linear prism.
|
||||
// If tapered, we create is using Part's draft extrusion method. If linear we create a prism.
|
||||
if (fabs(angle) > Base::toRadians(Precision::Angular()) || fabs(angle2) > Base::toRadians(Precision::Angular())) {
|
||||
// prism is tapered
|
||||
if (reversed)
|
||||
directionTaper.Reverse();
|
||||
generateTaperedPrism(prism, sketchshape, method, directionTaper, L, L2, angle, angle2, midplane);
|
||||
}
|
||||
else {
|
||||
// Without taper angle we create a prism because its shells are in every case no B-splines and can therefore
|
||||
// be use as support for further features like Pads, Lofts etc. B-spline shells can break certain features,
|
||||
// see e.g. https://forum.freecadweb.org/viewtopic.php?p=560785#p560785
|
||||
// It is better not to use BRepFeat_MakePrism here even if we have a support because the
|
||||
// resulting shape creates problems with Pocket
|
||||
BRepPrimAPI_MakePrism PrismMaker(from, Ltotal * gp_Vec(direction), 0, 1); // finite prism
|
||||
if (!PrismMaker.IsDone())
|
||||
throw Base::RuntimeError("ProfileBased: Length: Could not extrude the sketch!");
|
||||
prism = PrismMaker.Shape();
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::stringstream str;
|
||||
@@ -641,18 +662,17 @@ void ProfileBased::generatePrism(TopoDS_Shape& prism,
|
||||
<< method << "' for generatePrism()";
|
||||
throw Base::RuntimeError(str.str());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ProfileBased::generatePrism(TopoDS_Shape& prism,
|
||||
const std::string& method,
|
||||
const TopoDS_Shape& baseshape,
|
||||
const TopoDS_Shape& profileshape,
|
||||
const TopoDS_Face& supportface,
|
||||
const TopoDS_Face& uptoface,
|
||||
const gp_Dir& direction,
|
||||
PrismMode Mode,
|
||||
Standard_Boolean Modify)
|
||||
void ProfileBased::Extrude(TopoDS_Shape& prism,
|
||||
const std::string& method,
|
||||
const TopoDS_Shape& baseshape,
|
||||
const TopoDS_Shape& profileshape,
|
||||
const TopoDS_Face& supportface,
|
||||
const TopoDS_Face& uptoface,
|
||||
const gp_Dir& direction,
|
||||
PrismMode Mode,
|
||||
Standard_Boolean Modify)
|
||||
{
|
||||
if (method == "UpToFirst" || method == "UpToFace" || method == "UpToLast") {
|
||||
BRepFeat_MakePrism PrismMaker;
|
||||
@@ -678,6 +698,51 @@ void ProfileBased::generatePrism(TopoDS_Shape& prism,
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileBased::generateTaperedPrism(TopoDS_Shape& prism,
|
||||
const TopoDS_Shape& sketchshape,
|
||||
const std::string& method,
|
||||
const gp_Dir& direction,
|
||||
const double L,
|
||||
const double L2,
|
||||
const double angle,
|
||||
const double angle2,
|
||||
const bool midplane)
|
||||
{
|
||||
std::list<TopoDS_Shape> drafts;
|
||||
bool isSolid = true; // in PD we only generate solids, while Part Extrude can also create only shells
|
||||
bool isPartDesign = true; // there is an OCC bug with single-edge wires (circles) we need to treat differently for PD and Part
|
||||
if (method == "ThroughAll")
|
||||
Part::ExtrusionHelper::makeDraft(sketchshape, direction, getThroughAllLength(),
|
||||
0.0, Base::toRadians(angle), 0.0, isSolid, drafts, isPartDesign);
|
||||
else if (method == "TwoLengths")
|
||||
Part::ExtrusionHelper::makeDraft(sketchshape, direction, L, L2,
|
||||
Base::toRadians(angle), Base::toRadians(angle2), isSolid, drafts, isPartDesign);
|
||||
else if (method == "Length") {
|
||||
if (midplane) {
|
||||
Part::ExtrusionHelper::makeDraft(sketchshape, direction, L / 2, L / 2,
|
||||
Base::toRadians(angle), Base::toRadians(angle), isSolid, drafts, isPartDesign);
|
||||
}
|
||||
else
|
||||
Part::ExtrusionHelper::makeDraft(sketchshape, direction, L, 0.0,
|
||||
Base::toRadians(angle), 0.0, isSolid, drafts, isPartDesign);
|
||||
}
|
||||
|
||||
if (drafts.empty()) {
|
||||
throw Base::RuntimeError("Creation of tapered object failed");
|
||||
}
|
||||
else if (drafts.size() == 1) {
|
||||
prism = drafts.front();
|
||||
}
|
||||
else {
|
||||
TopoDS_Compound comp;
|
||||
BRep_Builder builder;
|
||||
builder.MakeCompound(comp);
|
||||
for (std::list<TopoDS_Shape>::iterator it = drafts.begin(); it != drafts.end(); ++it)
|
||||
builder.Add(comp, *it);
|
||||
prism = comp;
|
||||
}
|
||||
}
|
||||
|
||||
bool ProfileBased::checkWireInsideFace(const TopoDS_Wire& wire, const TopoDS_Face& face,
|
||||
const gp_Dir& dir) {
|
||||
// Project wire onto the face (face, not surface! So limits of face apply)
|
||||
@@ -1034,7 +1099,6 @@ bool ProfileBased::isParallelPlane(const TopoDS_Shape & s1, const TopoDS_Shape &
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
double ProfileBased::getReversedAngle(const Base::Vector3d & b, const Base::Vector3d & v)
|
||||
{
|
||||
try {
|
||||
|
||||
@@ -143,28 +143,31 @@ protected:
|
||||
double offset);
|
||||
|
||||
/**
|
||||
* Generate a linear prism
|
||||
* It will be a stand-alone solid created with BRepPrimAPI_MakePrism
|
||||
* Generates an extrusion of the input sketchshape and stores it in the given &prism
|
||||
*/
|
||||
void generatePrism(TopoDS_Shape& prism,
|
||||
const TopoDS_Shape& sketchshape,
|
||||
const std::string& method,
|
||||
const gp_Dir& direction,
|
||||
const double L,
|
||||
const double L2,
|
||||
const bool midplane,
|
||||
const bool reversed);
|
||||
void Extrude(TopoDS_Shape& prism,
|
||||
const TopoDS_Shape& sketchshape,
|
||||
const std::string& method,
|
||||
const gp_Dir& direction,
|
||||
const double L,
|
||||
const double L2,
|
||||
const double angle,
|
||||
const double angle2,
|
||||
const bool midplane,
|
||||
const bool reversed);
|
||||
|
||||
// See BRepFeat_MakePrism
|
||||
enum PrismMode {
|
||||
CutFromBase = 0,
|
||||
FuseWithBase = 1,
|
||||
None = 2
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate a linear prism
|
||||
* Generates an extrusion of the input profileshape
|
||||
* It will be a stand-alone solid created with BRepFeat_MakePrism
|
||||
*/
|
||||
static void generatePrism(TopoDS_Shape& prism,
|
||||
static void Extrude(TopoDS_Shape& prism,
|
||||
const std::string& method,
|
||||
const TopoDS_Shape& baseshape,
|
||||
const TopoDS_Shape& profileshape,
|
||||
@@ -174,6 +177,19 @@ protected:
|
||||
PrismMode Mode,
|
||||
Standard_Boolean Modify);
|
||||
|
||||
/**
|
||||
* Generates a tapered prism of the input sketchshape and stores it in the given &prism
|
||||
*/
|
||||
void generateTaperedPrism(TopoDS_Shape& prism,
|
||||
const TopoDS_Shape& sketchshape,
|
||||
const std::string& method,
|
||||
const gp_Dir& direction,
|
||||
const double L,
|
||||
const double L2,
|
||||
const double angle,
|
||||
const double angle2,
|
||||
const bool midplane);
|
||||
|
||||
/// Check whether the wire after projection on the face is inside the face
|
||||
static bool checkWireInsideFace(const TopoDS_Wire& wire,
|
||||
const TopoDS_Face& face,
|
||||
|
||||
@@ -82,6 +82,7 @@
|
||||
# include <BRepLProp_SLProps.hxx>
|
||||
# include <BRepProj_Projection.hxx>
|
||||
# include <BRepBuilderAPI_MakeSolid.hxx>
|
||||
# include <BRepBuilderAPI_MakeWire.hxx>
|
||||
# include <BRepBuilderAPI_Sewing.hxx>
|
||||
# include <BRepBuilderAPI_MakePolygon.hxx>
|
||||
# include <BRepBuilderAPI_MakeFace.hxx>
|
||||
@@ -89,6 +90,7 @@
|
||||
# include <BRepExtrema_DistShapeShape.hxx>
|
||||
# include <BRepFilletAPI_MakeChamfer.hxx>
|
||||
# include <BRepOffsetAPI_DraftAngle.hxx>
|
||||
# include <BRepOffsetAPI_MakeOffset.hxx>
|
||||
# include <BRepOffsetAPI_ThruSections.hxx>
|
||||
# include <BRepPrimAPI_MakeBox.hxx>
|
||||
# include <BRepPrimAPI_MakeCylinder.hxx>
|
||||
|
||||
@@ -73,6 +73,8 @@ void TaskExtrudeParameters::setupDialog()
|
||||
Base::Quantity l = extrude->Length.getQuantityValue();
|
||||
Base::Quantity l2 = extrude->Length2.getQuantityValue();
|
||||
Base::Quantity off = extrude->Offset.getQuantityValue();
|
||||
Base::Quantity taper = extrude->TaperAngle.getQuantityValue();
|
||||
Base::Quantity taper2 = extrude->TaperAngle2.getQuantityValue();
|
||||
|
||||
bool alongNormal = extrude->AlongSketchNormal.getValue();
|
||||
bool useCustom = extrude->UseCustomVector.getValue();
|
||||
@@ -107,6 +109,14 @@ void TaskExtrudeParameters::setupDialog()
|
||||
ui->lengthEdit->setValue(l);
|
||||
ui->lengthEdit2->setValue(l2);
|
||||
ui->offsetEdit->setValue(off);
|
||||
ui->taperEdit->setMinimum(extrude->TaperAngle.getMinimum());
|
||||
ui->taperEdit->setMaximum(extrude->TaperAngle.getMaximum());
|
||||
ui->taperEdit->setSingleStep(extrude->TaperAngle.getStepSize());
|
||||
ui->taperEdit->setValue(taper);
|
||||
ui->taperEdit2->setMinimum(extrude->TaperAngle2.getMinimum());
|
||||
ui->taperEdit2->setMaximum(extrude->TaperAngle2.getMaximum());
|
||||
ui->taperEdit2->setSingleStep(extrude->TaperAngle2.getStepSize());
|
||||
ui->taperEdit2->setValue(taper2);
|
||||
|
||||
ui->checkBoxAlongDirection->setChecked(alongNormal);
|
||||
ui->checkBoxDirection->setChecked(useCustom);
|
||||
@@ -126,6 +136,8 @@ void TaskExtrudeParameters::setupDialog()
|
||||
ui->lengthEdit->bind(extrude->Length);
|
||||
ui->lengthEdit2->bind(extrude->Length2);
|
||||
ui->offsetEdit->bind(extrude->Offset);
|
||||
ui->taperEdit->bind(extrude->TaperAngle);
|
||||
ui->taperEdit2->bind(extrude->TaperAngle2);
|
||||
ui->XDirectionEdit->bind(App::ObjectIdentifier::parse(extrude, std::string("Direction.x")));
|
||||
ui->YDirectionEdit->bind(App::ObjectIdentifier::parse(extrude, std::string("Direction.y")));
|
||||
ui->ZDirectionEdit->bind(App::ObjectIdentifier::parse(extrude, std::string("Direction.z")));
|
||||
@@ -172,6 +184,10 @@ void TaskExtrudeParameters::readValuesFromHistory()
|
||||
ui->lengthEdit2->selectNumber();
|
||||
ui->offsetEdit->setToLastUsedValue();
|
||||
ui->offsetEdit->selectNumber();
|
||||
ui->taperEdit->setToLastUsedValue();
|
||||
ui->taperEdit->selectNumber();
|
||||
ui->taperEdit2->setToLastUsedValue();
|
||||
ui->taperEdit2->selectNumber();
|
||||
}
|
||||
|
||||
void TaskExtrudeParameters::connectSlots()
|
||||
@@ -184,6 +200,10 @@ void TaskExtrudeParameters::connectSlots()
|
||||
this, SLOT(onLength2Changed(double)));
|
||||
connect(ui->offsetEdit, SIGNAL(valueChanged(double)),
|
||||
this, SLOT(onOffsetChanged(double)));
|
||||
connect(ui->taperEdit, SIGNAL(valueChanged(double)),
|
||||
this, SLOT(onTaperChanged(double)));
|
||||
connect(ui->taperEdit2, SIGNAL(valueChanged(double)),
|
||||
this, SLOT(onTaper2Changed(double)));
|
||||
connect(ui->directionCB, SIGNAL(activated(int)),
|
||||
this, SLOT(onDirectionCBChanged(int)));
|
||||
connect(ui->checkBoxAlongDirection, SIGNAL(toggled(bool)),
|
||||
@@ -292,6 +312,20 @@ void TaskExtrudeParameters::onOffsetChanged(double len)
|
||||
tryRecomputeFeature();
|
||||
}
|
||||
|
||||
void TaskExtrudeParameters::onTaperChanged(double angle)
|
||||
{
|
||||
PartDesign::FeatureExtrude* extrude = static_cast<PartDesign::FeatureExtrude*>(vp->getObject());
|
||||
extrude->TaperAngle.setValue(angle);
|
||||
tryRecomputeFeature();
|
||||
}
|
||||
|
||||
void TaskExtrudeParameters::onTaper2Changed(double angle)
|
||||
{
|
||||
PartDesign::FeatureExtrude* extrude = static_cast<PartDesign::FeatureExtrude*>(vp->getObject());
|
||||
extrude->TaperAngle2.setValue(angle);
|
||||
tryRecomputeFeature();
|
||||
}
|
||||
|
||||
bool TaskExtrudeParameters::hasProfileFace(PartDesign::ProfileBased* profile) const
|
||||
{
|
||||
try {
|
||||
@@ -769,6 +803,8 @@ void TaskExtrudeParameters::changeEvent(QEvent *e)
|
||||
QSignalBlocker length(ui->lengthEdit);
|
||||
QSignalBlocker length2(ui->lengthEdit2);
|
||||
QSignalBlocker offset(ui->offsetEdit);
|
||||
QSignalBlocker taper(ui->taperEdit);
|
||||
QSignalBlocker taper2(ui->taperEdit2);
|
||||
QSignalBlocker xdir(ui->XDirectionEdit);
|
||||
QSignalBlocker ydir(ui->YDirectionEdit);
|
||||
QSignalBlocker zdir(ui->ZDirectionEdit);
|
||||
@@ -804,6 +840,8 @@ void TaskExtrudeParameters::saveHistory(void)
|
||||
ui->lengthEdit->pushToHistory();
|
||||
ui->lengthEdit2->pushToHistory();
|
||||
ui->offsetEdit->pushToHistory();
|
||||
ui->taperEdit->pushToHistory();
|
||||
ui->taperEdit2->pushToHistory();
|
||||
}
|
||||
|
||||
void TaskExtrudeParameters::applyParameters(QString facename)
|
||||
@@ -812,6 +850,8 @@ void TaskExtrudeParameters::applyParameters(QString facename)
|
||||
|
||||
ui->lengthEdit->apply();
|
||||
ui->lengthEdit2->apply();
|
||||
ui->taperEdit->apply();
|
||||
ui->taperEdit2->apply();
|
||||
FCMD_OBJ_CMD(obj, "UseCustomVector = " << (getCustom() ? 1 : 0));
|
||||
FCMD_OBJ_CMD(obj, "Direction = ("
|
||||
<< getXDirection() << ", " << getYDirection() << ", " << getZDirection() << ")");
|
||||
|
||||
@@ -84,6 +84,8 @@ protected Q_SLOTS:
|
||||
void onLengthChanged(double);
|
||||
void onLength2Changed(double);
|
||||
void onOffsetChanged(double);
|
||||
void onTaperChanged(double);
|
||||
void onTaper2Changed(double);
|
||||
void onDirectionCBChanged(int);
|
||||
void onAlongSketchNormalChanged(bool);
|
||||
void onDirectionToggled(bool);
|
||||
@@ -104,13 +106,13 @@ protected:
|
||||
App::PropertyLinkSub* propReferenceAxis;
|
||||
void getReferenceAxis(App::DocumentObject*& obj, std::vector<std::string>& sub) const;
|
||||
|
||||
double getOffset(void) const;
|
||||
bool getAlongSketchNormal(void) const;
|
||||
bool getCustom(void) const;
|
||||
std::string getReferenceAxis(void) const;
|
||||
double getXDirection(void) const;
|
||||
double getYDirection(void) const;
|
||||
double getZDirection(void) const;
|
||||
double getOffset(void) const;
|
||||
bool getReversed(void) const;
|
||||
bool getMidplane(void) const;
|
||||
int getMode(void) const;
|
||||
|
||||
@@ -55,6 +55,10 @@ TaskPadParameters::TaskPadParameters(ViewProviderPad *PadView, QWidget *parent,
|
||||
ui->lengthEdit2->setParamGrpPath(QByteArray("User parameter:BaseApp/History/PadLength2"));
|
||||
ui->offsetEdit->setEntryName(QByteArray("Offset"));
|
||||
ui->offsetEdit->setParamGrpPath(QByteArray("User parameter:BaseApp/History/PadOffset"));
|
||||
ui->taperEdit->setEntryName(QByteArray("TaperAngle"));
|
||||
ui->taperEdit->setParamGrpPath(QByteArray("User parameter:BaseApp/History/PadTaperAngle"));
|
||||
ui->taperEdit2->setEntryName(QByteArray("TaperAngle2"));
|
||||
ui->taperEdit2->setParamGrpPath(QByteArray("User parameter:BaseApp/History/PadTaperAngle2"));
|
||||
|
||||
setupDialog();
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>300</width>
|
||||
<height>436</height>
|
||||
<height>490</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -44,6 +44,9 @@
|
||||
<property name="keyboardTracking">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true">mm</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
@@ -61,6 +64,9 @@
|
||||
<property name="keyboardTracking">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true">mm</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
@@ -258,6 +264,30 @@ measured along the specified direction</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelTaperAngle">
|
||||
<property name="toolTip">
|
||||
<string>Angle to taper the extrusion</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Taper angle</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Gui::PrefQuantitySpinBox" name="taperEdit">
|
||||
<property name="keyboardTracking">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true">deg</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
@@ -272,6 +302,33 @@ measured along the specified direction</string>
|
||||
<property name="keyboardTracking">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true">mm</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelTaperAngle2">
|
||||
<property name="toolTip">
|
||||
<string>Angle to taper the extrusion</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>2nd taper angle</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Gui::PrefQuantitySpinBox" name="taperEdit2">
|
||||
<property name="keyboardTracking">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true">deg</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
||||
@@ -56,6 +56,10 @@ TaskPocketParameters::TaskPocketParameters(ViewProviderPocket *PocketView,QWidge
|
||||
ui->lengthEdit2->setParamGrpPath(QByteArray("User parameter:BaseApp/History/PocketLength2"));
|
||||
ui->offsetEdit->setEntryName(QByteArray("Offset"));
|
||||
ui->offsetEdit->setParamGrpPath(QByteArray("User parameter:BaseApp/History/PocketOffset"));
|
||||
ui->taperEdit->setEntryName(QByteArray("TaperAngle"));
|
||||
ui->taperEdit->setParamGrpPath(QByteArray("User parameter:BaseApp/History/PocketTaperAngle"));
|
||||
ui->taperEdit2->setEntryName(QByteArray("TaperAngle2"));
|
||||
ui->taperEdit2->setParamGrpPath(QByteArray("User parameter:BaseApp/History/PocketTaperAngle2"));
|
||||
|
||||
setupDialog();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user