Merge branch 'main' into bgbsww-toponamingMissingAppMethods

This commit is contained in:
bgbsww
2024-05-02 19:52:31 -04:00
committed by bgbsww
12 changed files with 504 additions and 167 deletions

View File

@@ -241,4 +241,4 @@ GeoFeature::getHigherElements(const char *element, bool silent) const
return {};
return prop->getComplexData()->getHigherElements(element, silent);
}
#endif
#endif

View File

@@ -103,12 +103,13 @@ bool PropertyLinkBase::isSame(const Property &other) const
void PropertyLinkBase::unregisterElementReference() {
#ifdef FC_USE_TNP_FIX
for(auto obj : _ElementRefs) {
for (auto obj : _ElementRefs) {
auto it = _ElementRefMap.find(obj);
if(it != _ElementRefMap.end()) {
if (it != _ElementRefMap.end()) {
it->second.erase(this);
if(it->second.empty())
if (it->second.empty()) {
_ElementRefMap.erase(it);
}
}
}
_ElementRefs.clear();
@@ -219,24 +220,28 @@ static std::string propertyName(const Property *prop) {
void PropertyLinkBase::updateElementReferences(DocumentObject *feature, bool reverse) {
#ifdef FC_USE_TNP_FIX
if(!feature || !feature->getNameInDocument())
if (!feature || !feature->getNameInDocument()) {
return;
}
auto it = _ElementRefMap.find(feature);
if(it == _ElementRefMap.end())
if (it == _ElementRefMap.end()) {
return;
}
std::vector<PropertyLinkBase*> props;
props.reserve(it->second.size());
props.insert(props.end(),it->second.begin(),it->second.end());
for(auto prop : props) {
if(prop->getContainer()) {
props.insert(props.end(), it->second.begin(), it->second.end());
for (auto prop : props) {
if (prop->getContainer()) {
try {
prop->updateElementReference(feature,reverse,true);
}catch(Base::Exception &e) {
prop->updateElementReference(feature, reverse, true);
}
catch (Base::Exception& e) {
e.ReportException();
FC_ERR("Failed to update element reference of " << propertyName(prop));
}catch(std::exception &e) {
FC_ERR("Failed to update element reference of " << propertyName(prop)
<< ": " << e.what());
}
catch (std::exception& e) {
FC_ERR("Failed to update element reference of " << propertyName(prop) << ": "
<< e.what());
}
}
}
@@ -249,22 +254,31 @@ void PropertyLinkBase::updateElementReferences(DocumentObject *feature, bool rev
void PropertyLinkBase::_registerElementReference(App::DocumentObject *obj, std::string &sub, ShadowSub &shadow)
{
#ifdef FC_USE_TNP_FIX
if(!obj || !obj->getNameInDocument() || sub.empty())
return;
if(shadow.first.empty()) {
_updateElementReference(0,obj,sub,shadow,false);
if (!obj || !obj->getNameInDocument() || sub.empty()) {
return;
}
GeoFeature *geo = 0;
const char *element = 0;
std::pair<std::string, std::string> elementName;
GeoFeature::resolveElement(obj,sub.c_str(), elementName,true,
GeoFeature::ElementNameType::Export,0,&element,&geo);
if(!geo || !element || !element[0])
if (shadow.first.empty()) {
_updateElementReference(0, obj, sub, shadow, false);
return;
}
GeoFeature* geo = nullptr;
const char* element = nullptr;
std::pair<std::string, std::string> elementName;
GeoFeature::resolveElement(obj,
sub.c_str(),
elementName,
true,
GeoFeature::ElementNameType::Export,
0,
&element,
&geo);
if (!geo || !element || !element[0]) {
return;
}
if(_ElementRefs.insert(geo).second)
if (_ElementRefs.insert(geo).second) {
_ElementRefMap[geo].insert(this);
}
#else
(void)obj;
(void)sub;
@@ -335,17 +349,22 @@ bool PropertyLinkBase::_updateElementReference(DocumentObject *feature,
bool reverse, bool notify)
{
#ifdef FC_USE_TNP_FIX
if (!obj || !obj->getNameInDocument()) return false;
if (!obj || !obj->getNameInDocument()) {
return false;
}
ShadowSub elementName;
const char* subname;
if (shadow.first.size())
if (shadow.first.size()) {
subname = shadow.first.c_str();
else if (shadow.second.size())
}
else if (shadow.second.size()) {
subname = shadow.second.c_str();
else
}
else {
subname = sub.c_str();
GeoFeature* geo = 0;
const char* element = 0;
}
GeoFeature* geo = nullptr;
const char* element = nullptr;
auto ret = GeoFeature::resolveElement(obj,
subname,
elementName,
@@ -355,22 +374,25 @@ bool PropertyLinkBase::_updateElementReference(DocumentObject *feature,
&element,
&geo);
if (!ret || !geo || !element || !element[0]) {
if (elementName.second.size())
if (elementName.second.size()) {
shadow.second.swap(elementName.second);
}
return false;
}
if (_ElementRefs.insert(geo).second)
_ElementRefMap[geo].insert(this);
if (_ElementRefs.insert(geo).second) {
_ElementRefMap[geo].insert(this);
}
if (!reverse) {
if (!reverse) {
if (elementName.first.empty()) {
shadow.second.swap(elementName.second);
return false;
}
if (shadow == elementName)
return false;
if (shadow == elementName) {
return false;
}
}
bool missing = Data::hasMissingElement(elementName.second.c_str());
if (feature == geo && (missing || reverse)) {
@@ -379,8 +401,8 @@ bool PropertyLinkBase::_updateElementReference(DocumentObject *feature,
// to version change, i.e. 'reverse', try search by geometry first
const char* oldElement = Data::findElementName(shadow.second.c_str());
if (!Data::hasMissingElement(oldElement)) {
// const auto& names = geo->searchElementCache(oldElement);
std::vector<std::string> names; // searchElementCache isn't implemented.
// const auto& names = geo->searchElementCache(oldElement);
std::vector<std::string> names; // searchElementCache isn't implemented.
if (names.size()) {
missing = false;
std::string newsub(subname, strlen(subname) - strlen(element));
@@ -403,12 +425,13 @@ bool PropertyLinkBase::_updateElementReference(DocumentObject *feature,
}
}
if (notify)
aboutToSetValue();
if (notify) {
aboutToSetValue();
}
auto updateSub = [&](const std::string& newSub) {
auto updateSub = [&](const std::string& newSub) {
if (sub != newSub) {
//signalUpdateElementReference(sub, newSub);
// signalUpdateElementReference(sub, newSub);
sub = newSub;
}
};
@@ -423,32 +446,39 @@ bool PropertyLinkBase::_updateElementReference(DocumentObject *feature,
FC_TRACE(propertyName(this) << " element reference shadow update " << ret->getFullName()
<< " " << shadow.first << " -> " << elementName.first);
shadow.swap(elementName);
if (shadow.first.size() && Data::hasMappedElementName(sub.c_str()))
updateSub(shadow.first);
if (shadow.first.size() && Data::hasMappedElementName(sub.c_str())) {
updateSub(shadow.first);
}
}
if (reverse) {
if (shadow.first.size() && Data::hasMappedElementName(sub.c_str()))
updateSub(shadow.first);
else
updateSub(shadow.second);
return true;
if (shadow.first.size() && Data::hasMappedElementName(sub.c_str())) {
updateSub(shadow.first);
}
else {
updateSub(shadow.second);
}
return true;
}
if (missing) {
if (sub != shadow.first)
updateSub(shadow.second);
return true;
if (sub != shadow.first) {
updateSub(shadow.second);
}
return true;
}
auto pos2 = shadow.first.rfind('.');
if (pos2 == std::string::npos)
return true;
++pos2;
if (pos2 == std::string::npos) {
return true;
}
++pos2;
auto pos = sub.rfind('.');
if (pos == std::string::npos)
pos = 0;
else
++pos;
if (pos == pos2) {
if (pos == std::string::npos) {
pos = 0;
}
else {
++pos;
}
if (pos == pos2) {
if (sub.compare(pos, sub.size() - pos, &shadow.first[pos2]) != 0) {
FC_LOG("element reference update " << sub << " -> " << shadow.first);
std::string newSub(sub);
@@ -478,52 +508,62 @@ PropertyLinkBase::tryReplaceLink(const PropertyContainer *owner, DocumentObject
std::pair<DocumentObject*, std::string> res;
res.first = 0;
if(oldObj == obj) {
if(owner == parent) {
if (oldObj == obj) {
if (owner == parent) {
res.first = newObj;
if(subname) res.second = subname;
if (subname) {
res.second = subname;
}
return res;
}
return res;
#ifdef FC_USE_TNP_FIX
} else if (newObj == obj) {
}
else if (newObj == obj) {
// This means the new object is already sub-object of this parent
// (consider a case of swapping the tool and base object of the Cut
// feature). We'll swap the old and new object.
return tryReplaceLink(owner, obj, parent, newObj, oldObj, subname);
#endif
}
if(!subname || !subname[0])
if (!subname || !subname[0]) {
return res;
}
App::DocumentObject *prev = obj;
App::DocumentObject* prev = obj;
std::size_t prevPos = 0;
std::string sub = subname;
for(auto pos=sub.find('.');pos!=std::string::npos;pos=sub.find('.',pos)) {
for (auto pos = sub.find('.'); pos != std::string::npos; pos = sub.find('.', pos)) {
++pos;
char c = sub[pos];
sub[pos] = 0;
auto sobj = obj->getSubObject(sub.c_str());
sub[pos] = c;
if(!sobj)
if (!sobj) {
break;
if(sobj == oldObj) {
if(prev == parent) {
if(sub[prevPos] == '$')
sub.replace(prevPos+1,pos-1-prevPos,newObj->Label.getValue());
else
sub.replace(prevPos,pos-1-prevPos,newObj->getNameInDocument());
}
if (sobj == oldObj) {
if (prev == parent) {
if (sub[prevPos] == '$') {
sub.replace(prevPos + 1, pos - 1 - prevPos, newObj->Label.getValue());
}
else {
sub.replace(prevPos, pos - 1 - prevPos, newObj->getNameInDocument());
}
res.first = obj;
res.second = std::move(sub);
return res;
}
break;
#ifdef FC_USE_TNP_FIX
}else if(sobj == newObj) {
}
else if (sobj == newObj) {
return tryReplaceLink(owner, obj, parent, newObj, oldObj, subname);
#endif
}else if(prev == parent)
}
else if (prev == parent) {
break;
}
prev = sobj;
prevPos = pos;
}
@@ -580,7 +620,6 @@ TYPESYSTEM_SOURCE(App::PropertyLinkHidden , App::PropertyLink)
PropertyLink::PropertyLink() = default;
PropertyLink::~PropertyLink()
{
resetLink();

View File

@@ -259,6 +259,7 @@ class CurtainWall(ArchComponent.Component):
obj.addProperty("App::PropertyVector","VerticalDirection","CurtainWall",
QT_TRANSLATE_NOOP("App::Property","The vertical direction reference to be used by this object to deduce vertical/horizontal directions. Keep it close to the actual vertical direction of your curtain wall"))
obj.VerticalDirection = FreeCAD.Vector(0,0,1)
self.Type = "CurtainWall"
def onDocumentRestored(self,obj):

View File

@@ -77,6 +77,7 @@ class Line(gui_base_original.Creator):
self.obj = self.doc.addObject("Part::Feature", self.featureName)
gui_utils.format_object(self.obj)
self.obj.ViewObject.ShowInTree = False
self.call = self.view.addEventCallback("SoEvent", self.action)
_toolmsg(translate("draft", "Pick first point"))

View File

@@ -45,8 +45,7 @@ class ViewProviderPoint(ViewProviderDraft):
vobj.setEditorMode('DisplayMode', mode)
vobj.setEditorMode('Lighting', mode)
vobj.setEditorMode('LineMaterial', mode)
vobj.setEditorMode('ShapeColor', mode)
vobj.setEditorMode('ShapeMaterial', mode)
vobj.setEditorMode('ShapeAppearance', mode)
vobj.setEditorMode('Transparency', mode)
def getIcon(self):

View File

@@ -28,5 +28,5 @@
using namespace std;
void CreateMeasureCommands() {
Base::Console().Message("Init MeasureGui\n");
Base::Console().Log("Init MeasureGui\n");
}

View File

@@ -261,6 +261,13 @@ enum class OpenResult
allowOpenResult
};
// See BRepFeat_MakeRevol
enum class RevolMode {
CutFromBase = 0,
FuseWithBase = 1,
None = 2
};
/** The representation for a CAD Shape
*/
// NOLINTNEXTLINE cppcoreguidelines-special-member-functions
@@ -1081,7 +1088,6 @@ public:
/** Make revolved shell around a basis shape
*
* @param base: the basis shape
* @param axis: the revolving axis
* @param d: rotation angle in degree
* @param face_maker: optional type name of the the maker used to make a
@@ -1097,6 +1103,67 @@ public:
}
/** Make revolved shell around a basis shape
*
* @param base: the basis shape
* @param axis: the revolving axis
* @param d: rotation angle in degree
* @param face_maker: optional type name of the the maker used to make a
* face from basis shape
* @param supportface: the bottom face for the revolution, or null
* @param uptoface: the upper limit face for the revolution, or null
* @param Mode: the opencascade defined modes
* @param Modify: if opencascade should modify existing shapes
* @param op: optional string to be encoded into topo naming for indicating
* the operation
*
* @return Return the generated new shape. The TopoShape itself is not modified.
*/
TopoShape& makeElementRevolution(const TopoShape& _base,
const gp_Ax1& axis,
double d,
const TopoDS_Face& supportface,
const TopoDS_Face& uptoface,
const char* face_maker = nullptr,
RevolMode Mode = RevolMode::None,
Standard_Boolean Modify = Standard_True,
const char* op = nullptr);
/** Make revolved shell around a basis shape
*
* @param axis: the revolving axis
* @param d: rotation angle in degree
* @param face_maker: optional type name of the the maker used to make a
* face from basis shape
* @param supportface: the bottom face for the revolution, or null
* @param uptoface: the upper limit face for the revolution, or null
* @param Mode: the opencascade defined modes
* @param Modify: if opencascade should modify existing shapes
* @param op: optional string to be encoded into topo naming for indicating
* the operation
*
* @return Return the generated new shape. The TopoShape itself is not modified.
*/
TopoShape& makeElementRevolution(const gp_Ax1& axis,
double d,
const TopoDS_Face& supportface,
const TopoDS_Face& uptoface,
const char* face_maker = nullptr,
RevolMode Mode = RevolMode::None,
Standard_Boolean Modify = Standard_True,
const char* op = nullptr) const
{
return TopoShape(0, Hasher).makeElementRevolution(*this,
axis,
d,
supportface,
uptoface,
face_maker,
Mode,
Modify,
op);
}
/** Make a prism that is a linear sweep of a basis shape
*
* @param base: the basis shape

View File

@@ -106,6 +106,7 @@
#include <App/ElementNamingUtils.h>
#include <ShapeAnalysis_FreeBoundsProperties.hxx>
#include <BRepBuilderAPI_MakeSolid.hxx>
#include <BRepFeat_MakeRevol.hxx>
FC_LOG_LEVEL_INIT("TopoShape", true, true) // NOLINT
@@ -4439,6 +4440,51 @@ TopoShape& TopoShape::makeElementRevolve(const TopoShape& _base,
return makeElementShape(mkRevol, base, op);
}
TopoShape& TopoShape::makeElementRevolution(const TopoShape& _base,
const gp_Ax1& axis,
double d,
const TopoDS_Face& supportface,
const TopoDS_Face& uptoface,
const char* face_maker,
RevolMode Mode,
Standard_Boolean Modify,
const char* op)
{
if (!op) {
op = Part::OpCodes::Revolve;
}
TopoShape base(_base);
if (base.isNull()) {
FC_THROWM(NullShapeException, "Null shape");
}
if (face_maker && !base.hasSubShape(TopAbs_FACE)) {
if (!base.hasSubShape(TopAbs_WIRE)) {
base = base.makeElementWires();
}
base = base.makeElementFace(nullptr, face_maker, nullptr);
}
BRepFeat_MakeRevol mkRevol;
for (TopExp_Explorer xp(base.getShape(), TopAbs_FACE); xp.More(); xp.Next()) {
mkRevol.Init(_base.getShape(),
xp.Current(),
supportface,
axis,
static_cast<int>(Mode),
Modify);
mkRevol.Perform(uptoface);
if (!mkRevol.IsDone()) {
throw Base::RuntimeError("Revolution: Up to face: Could not revolve the sketch!");
}
base = mkRevol.Shape();
if (Mode == RevolMode::None) {
Mode = RevolMode::FuseWithBase;
}
}
return makeElementShape(mkRevol, base, op);
}
TopoShape& TopoShape::makeElementDraft(const TopoShape& shape,
const std::vector<TopoShape>& _faces,
const gp_Dir& pullDirection,

View File

@@ -38,6 +38,7 @@
#include <Base/Tools.h>
#include "FeatureRevolution.h"
#include "Mod/Part/App/TopoShapeOpCode.h"
using namespace PartDesign;
@@ -78,24 +79,29 @@ short Revolution::mustExecute() const
return ProfileBased::mustExecute();
}
App::DocumentObjectExecReturn *Revolution::execute()
App::DocumentObjectExecReturn* Revolution::execute()
{
// Validate parameters
// All angles are in radians unless explicitly stated
double angleDeg = Angle.getValue();
if (angleDeg > 360.0)
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Angle of revolution too large"));
if (angleDeg > 360.0) {
return new App::DocumentObjectExecReturn(
QT_TRANSLATE_NOOP("Exception", "Angle of revolution too large"));
}
double angle = Base::toRadians<double>(angleDeg);
if (angle < Precision::Angular())
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Angle of revolution too small"));
if (angle < Precision::Angular()) {
return new App::DocumentObjectExecReturn(
QT_TRANSLATE_NOOP("Exception", "Angle of revolution too small"));
}
double angle2 = Base::toRadians(Angle2.getValue());
TopoDS_Shape sketchshape;
TopoShape sketchshape;
try {
sketchshape = getVerifiedFace();
} catch (const Base::Exception& e) {
}
catch (const Base::Exception& e) {
return new App::DocumentObjectExecReturn(e.what());
}
@@ -103,27 +109,36 @@ App::DocumentObjectExecReturn *Revolution::execute()
TopoShape base;
try {
base = getBaseTopoShape();
} catch (const Base::Exception&) {
}
catch (const Base::Exception&) {
// fall back to support (for legacy features)
base = TopoShape();
}
// update Axis from ReferenceAxis
try {
updateAxis();
} catch (const Base::Exception& e) {
}
catch (const Base::Exception& e) {
return new App::DocumentObjectExecReturn(e.what());
}
// get revolve axis
Base::Vector3d b = Base.getValue();
gp_Pnt pnt(b.x,b.y,b.z);
Base::Vector3d v = Axis.getValue();
gp_Dir dir(v.x,v.y,v.z);
try {
if (sketchshape.IsNull())
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Creating a face from sketch failed"));
// get revolve axis
Base::Vector3d b = Base.getValue();
gp_Pnt pnt(b.x, b.y, b.z);
Base::Vector3d v = Axis.getValue();
if (v.IsNull()) {
return new App::DocumentObjectExecReturn(
QT_TRANSLATE_NOOP("Exception", "Reference axis is invalid"));
}
gp_Dir dir(v.x, v.y, v.z);
if (sketchshape.isNull()) {
return new App::DocumentObjectExecReturn(
QT_TRANSLATE_NOOP("Exception", "Creating a face from sketch failed"));
}
RevolMethod method = methodFromString(Type.getValueAsString());
@@ -132,51 +147,99 @@ App::DocumentObjectExecReturn *Revolution::execute()
pnt.Transform(invObjLoc.Transformation());
dir.Transform(invObjLoc.Transformation());
base.move(invObjLoc);
sketchshape.Move(invObjLoc);
sketchshape.move(invObjLoc);
// Check distance between sketchshape and axis - to avoid failures and crashes
TopExp_Explorer xp;
xp.Init(sketchshape, TopAbs_FACE);
for (;xp.More(); xp.Next()) {
if (checkLineCrossesFace(gp_Lin(pnt, dir), TopoDS::Face(xp.Current())))
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Revolve axis intersects the sketch"));
xp.Init(sketchshape.getShape(), TopAbs_FACE);
for (; xp.More(); xp.Next()) {
if (checkLineCrossesFace(gp_Lin(pnt, dir), TopoDS::Face(xp.Current()))) {
return new App::DocumentObjectExecReturn(
QT_TRANSLATE_NOOP("Exception", "Revolve axis intersects the sketch"));
}
}
// Create a fresh support even when base exists so that it can be used for patterns
#ifdef FC_USE_TNP_FIX
TopoShape result;
#else
TopoDS_Shape result;
#endif
TopoDS_Face supportface = getSupportFace();
supportface.Move(invObjLoc);
if (method == RevolMethod::ToFace || method == RevolMethod::ToFirst || method == RevolMethod::ToLast) {
if (method == RevolMethod::ToFace || method == RevolMethod::ToFirst
|| method == RevolMethod::ToLast) {
TopoDS_Face upToFace;
if (method == RevolMethod::ToFace) {
getFaceFromLinkSub(upToFace, UpToFace);
upToFace.Move(invObjLoc);
}
else
throw Base::RuntimeError("ProfileBased: Revolution up to first/last is not yet supported");
else {
throw Base::RuntimeError(
"ProfileBased: Revolution up to first/last is not yet supported");
}
// TODO: This method is designed for extrusions. needs to be adapted for revolutions.
// getUpToFace(upToFace, base, supportface, sketchshape, method, dir);
TopoDS_Face supportface = getSupportFace();
// TopoDS_Face supportface = getSupportFace();
supportface.Move(invObjLoc);
if (Reversed.getValue())
if (Reversed.getValue()) {
dir.Reverse();
}
TopExp_Explorer Ex(supportface,TopAbs_WIRE);
if (!Ex.More())
TopExp_Explorer Ex(supportface, TopAbs_WIRE);
if (!Ex.More()) {
supportface = TopoDS_Face();
}
RevolMode mode = RevolMode::None;
generateRevolution(result, base.getShape(), sketchshape, supportface, upToFace, gp_Ax1(pnt, dir), method, mode, Standard_True);
#ifdef FC_USE_TNP_FIX
// revolve the face to a solid
// TopoShape result(0);
try {
result = base.makeElementRevolution(gp_Ax1(pnt, dir), angle, supportface, upToFace);
}
catch (Standard_Failure&) {
return new App::DocumentObjectExecReturn("Could not revolve the sketch!");
}
#else
generateRevolution(result,
base.getShape(),
sketchshape.getShape(),
supportface,
upToFace,
gp_Ax1(pnt, dir),
method,
mode,
Standard_True);
#endif
}
else {
bool midplane = Midplane.getValue();
bool reversed = Reversed.getValue();
generateRevolution(result, sketchshape, gp_Ax1(pnt, dir), angle, angle2, midplane, reversed, method);
generateRevolution(result,
sketchshape.getShape(),
gp_Ax1(pnt, dir),
angle,
angle2,
midplane,
reversed,
method);
}
#ifdef FC_USE_TNP_FIX
if (!result.isNull()) {
result = refineShapeIfActive(result);
// set the additive shape property for later usage in e.g. pattern
this->AddSubShape.setValue(result);
if (!base.isNull()) {
result = result.makeElementFuse(base);
result = refineShapeIfActive(result);
}
#else
if (!result.IsNull()) {
result = refineShapeIfActive(result);
// set the additive shape property for later usage in e.g. pattern
@@ -186,16 +249,21 @@ App::DocumentObjectExecReturn *Revolution::execute()
// Let's call algorithm computing a fuse operation:
BRepAlgoAPI_Fuse mkFuse(base.getShape(), result);
// Let's check if the fusion has been successful
if (!mkFuse.IsDone())
throw Part::BooleanException(QT_TRANSLATE_NOOP("Exception", "Fusion with base feature failed"));
if (!mkFuse.IsDone()) {
throw Part::BooleanException(
QT_TRANSLATE_NOOP("Exception", "Fusion with base feature failed"));
}
result = mkFuse.Shape();
result = refineShapeIfActive(result);
}
#endif
this->Shape.setValue(getSolid(result));
}
else
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Could not revolve the sketch!"));
else {
return new App::DocumentObjectExecReturn(
QT_TRANSLATE_NOOP("Exception", "Could not revolve the sketch!"));
}
// eventually disable some settings that are not valid for the current method
updateProperties(method);
@@ -204,11 +272,15 @@ App::DocumentObjectExecReturn *Revolution::execute()
}
catch (Standard_Failure& e) {
if (std::string(e.GetMessageString()) == "TopoDS::Face")
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Could not create face from sketch.\n"
"Intersecting sketch entities in a sketch are not allowed."));
else
if (std::string(e.GetMessageString()) == "TopoDS::Face") {
return new App::DocumentObjectExecReturn(
QT_TRANSLATE_NOOP("Exception",
"Could not create face from sketch.\n"
"Intersecting sketch entities in a sketch are not allowed."));
}
else {
return new App::DocumentObjectExecReturn(e.GetMessageString());
}
}
catch (Base::Exception& e) {
return new App::DocumentObjectExecReturn(e.what());
@@ -257,7 +329,12 @@ Revolution::RevolMethod Revolution::methodFromString(const std::string& methodSt
return RevolMethod::Dimension;
}
#ifdef FC_USE_TNP_FIX
void Revolution::generateRevolution(TopoShape& revol,
#else
void Revolution::generateRevolution(TopoDS_Shape& revol,
#endif
const TopoDS_Shape& sketchshape,
const gp_Ax1& axis,
const double angle,
@@ -267,46 +344,49 @@ void Revolution::generateRevolution(TopoDS_Shape& revol,
RevolMethod method)
{
if (method == RevolMethod::Dimension || method == RevolMethod::TwoDimensions || method == RevolMethod::ThroughAll) {
double angleTotal = angle;
double angleOffset = 0.;
double angleTotal = angle;
double angleOffset = 0.;
if (method == RevolMethod::TwoDimensions) {
// Rotate the face by `angle2`/`angle` to get "second" angle
angleTotal += angle2;
angleOffset = angle2 * -1.0;
}
else if (midplane) {
// Rotate the face by half the angle to get Revolution symmetric to sketch plane
angleOffset = -angle / 2;
}
if (method == RevolMethod::TwoDimensions) {
// Rotate the face by `angle2`/`angle` to get "second" angle
angleTotal += angle2;
angleOffset = angle2 * -1.0;
}
else if (midplane) {
// Rotate the face by half the angle to get Revolution symmetric to sketch plane
angleOffset = -angle / 2;
}
if (fabs(angleTotal) < Precision::Angular())
throw Base::ValueError("Cannot create a revolution with zero angle.");
if (fabs(angleTotal) < Precision::Angular())
throw Base::ValueError("Cannot create a revolution with zero angle.");
gp_Ax1 revolAx(axis);
if (reversed) {
revolAx.Reverse();
}
gp_Ax1 revolAx(axis);
if (reversed) {
revolAx.Reverse();
}
TopoDS_Shape from = sketchshape;
if (method == RevolMethod::TwoDimensions || midplane) {
gp_Trsf mov;
mov.SetRotation(revolAx, angleOffset);
TopLoc_Location loc(mov);
from.Move(loc);
}
TopoDS_Shape from = sketchshape;
if (method == RevolMethod::TwoDimensions || midplane) {
gp_Trsf mov;
mov.SetRotation(revolAx, angleOffset);
TopLoc_Location loc(mov);
from.Move(loc);
}
// revolve the face to a solid
// BRepPrimAPI is the only option that allows use of this shape for patterns.
// See https://forum.freecadweb.org/viewtopic.php?f=8&t=70185&p=611673#p611673.
BRepPrimAPI_MakeRevol RevolMaker(from, revolAx, angleTotal);
#ifdef FC_USE_TNP_FIX
revol = TopoShape(from).makeElementRevolve(revolAx,angleTotal);
#else
// revolve the face to a solid
// BRepPrimAPI is the only option that allows use of this shape for patterns.
// See https://forum.freecadweb.org/viewtopic.php?f=8&t=70185&p=611673#p611673.
BRepPrimAPI_MakeRevol RevolMaker(from, revolAx, angleTotal);
if (!RevolMaker.IsDone())
throw Base::RuntimeError("ProfileBased: RevolMaker failed! Could not revolve the sketch!");
else
revol = RevolMaker.Shape();
}
else {
if (!RevolMaker.IsDone())
throw Base::RuntimeError("ProfileBased: RevolMaker failed! Could not revolve the sketch!");
else
revol = RevolMaker.Shape();
#endif
} else {
std::stringstream str;
str << "ProfileBased: Internal error: Unknown method for generateRevolution()";
throw Base::RuntimeError(str.str());

View File

@@ -95,7 +95,11 @@ protected:
/**
* Generates a revolution of the input sketchshape and stores it in the given \a revol.
*/
#ifdef FC_USE_TNP_FIX
void generateRevolution(TopoShape& revol,
#else
void generateRevolution(TopoDS_Shape& revol,
#endif
const TopoDS_Shape& sketchshape,
const gp_Ax1& ax1,
const double angle,

View File

@@ -498,14 +498,7 @@ class TestTopologicalNamingProblem(unittest.TestCase):
self.Doc.recompute()
# Assert
# self.assertEqual(len(body.Shape.childShapes()), 1)
if App.GuiUp:
# Todo: This triggers a 'hasher mismatch' warning in TopoShape::mapSubElement as called by
# flushElementMap. This appears to be the case whenever you have a parent with a hasher
# that has children without hashmaps. The warning seems to be spurious in this case, but
# perhaps there is a solution involving setting hashmaps on all elements.
self.assertEqual(body.Shape.childShapes()[0].ElementMapSize, 30)
else:
self.assertEqual(body.Shape.childShapes()[0].ElementMapSize, 26)
self.assertEqual(body.Shape.childShapes()[0].ElementMapSize, 30)
self.assertEqual(body.Shape.ElementMapSize,30)
self.assertEqual(sketch.Shape.ElementMapSize,12)
self.assertEqual(pad.Shape.ElementMapSize,30)
@@ -520,7 +513,7 @@ class TestTopologicalNamingProblem(unittest.TestCase):
return
# Act
revolution = self.Doc.addObject('PartDesign::Revolution', 'Revolution')
revolution.ReferenceAxis = body.Origin.OriginFeatures[1]
revolution.ReferenceAxis = (self.Doc.getObject('Sketch'),['V_Axis'])
revolution.Profile = sketch # Causing segfault
body.addObject(sketch)
body.addObject(revolution)
@@ -582,7 +575,7 @@ class TestTopologicalNamingProblem(unittest.TestCase):
# Act
helix = self.Doc.addObject('PartDesign::AdditiveHelix', 'Helix')
helix.Profile = sketch
helix.ReferenceAxis = body.Origin.OriginFeatures[2]
helix.ReferenceAxis = (self.Doc.getObject('Sketch'),['V_Axis'])
body.addObject(sketch)
body.addObject(helix)
self.Doc.recompute()
@@ -880,6 +873,83 @@ class TestTopologicalNamingProblem(unittest.TestCase):
self.assertEqual(self.Body.Shape.BoundBox.YMax,25.2)
self.assertEqual(self.Body.Shape.BoundBox.ZMax,20)
def testApplyFillet(self):
# Arrange
self.Body = self.Doc.addObject('PartDesign::Body', 'Body')
# Make first offset cube Pad
self.PadSketch = self.Doc.addObject('Sketcher::SketchObject', 'Sketch')
self.Body.addObject(self.PadSketch)
TestSketcherApp.CreateRectangleSketch(self.PadSketch, (0, 0), (31.37, 25.2))
self.Doc.recompute()
self.Pad = self.Doc.addObject("PartDesign::Pad", "Pad")
self.Body.addObject(self.Pad)
self.Pad.Profile = self.PadSketch
self.Pad.Length = 10
self.Doc.recompute()
self.Sketch001 = self.Body.newObject('Sketcher::SketchObject','Sketch001')
self.Sketch001.AttachmentSupport = (self.Doc.getObject('Pad'),['Face6',])
self.Sketch001.MapMode = 'FlatFace'
App.ActiveDocument.recompute()
self.Sketch001.addExternal("Pad","Edge10")
self.Sketch001.addExternal("Pad","Edge7")
geoList = []
geoList.append(Part.Circle(App.Vector(15.093666, 13.036922, 0.000000),
App.Vector(0.000000, 0.000000, 1.000000), 5.000000))
self.Sketch001.addGeometry(geoList,False)
del geoList
self.Sketch001.addConstraint(Sketcher.Constraint('Radius',0,5.000000))
self.Sketch001.addConstraint(Sketcher.Constraint('Symmetric',-3,2,-4,1,0,3))
App.ActiveDocument.recompute()
self.Doc.recompute()
self.Pad001 = self.Body.newObject('PartDesign::Pad','Pad001')
self.Pad001.Profile = self.Doc.getObject('Sketch001')
self.Pad001.Length = 10
App.ActiveDocument.recompute()
self.Pad001.ReferenceAxis = (self.Doc.getObject('Sketch001'),['N_Axis'])
self.Sketch001.Visibility = False
App.ActiveDocument.recompute()
self.Pad001.Length = 10.000000
self.Pad001.TaperAngle = 0.000000
self.Pad001.UseCustomVector = 0
self.Pad001.Direction = (0, 0, 1)
self.Pad001.ReferenceAxis = (self.Doc.getObject('Sketch001'), ['N_Axis'])
self.Pad001.AlongSketchNormal = 1
self.Pad001.Type = 0
self.Pad001.UpToFace = None
self.Pad001.Reversed = 0
self.Pad001.Midplane = 0
self.Pad001.Offset = 0
self.Doc.recompute()
self.Doc.getObject('Pad').Visibility = False
self.Doc.getObject('Sketch001').Visibility = False
area1 = self.Pad.Shape.Area
# Act
self.Doc.getObject('Sketch').fillet(2,3,App.Vector(6.673934,25.000000,0),App.Vector(0.000000,21.980343,0),4.740471,True,True,False)
self.Doc.recompute()
# filleted = self.Pad001.Shape.makeFillet(1,self.Pad001.Shape.Edges[0:2])
# self.filleted = Part.show(filleted,"Filleted")
# self.Body.addObject(self.filleted)
area2 = self.Pad.Shape.Area
print(area1,area2)
# Assert
if self.Body.Shape.ElementMapVersion == "": # Should be '4' as of Mar 2023.
return
self.assertEqual(self.Body.Shape.BoundBox.XMin,0)
self.assertEqual(self.Body.Shape.BoundBox.YMin,0)
self.assertEqual(self.Body.Shape.BoundBox.ZMin,0)
self.assertEqual(self.Body.Shape.BoundBox.XMax,31.37)
self.assertEqual(self.Body.Shape.BoundBox.YMax,25.2)
self.assertEqual(self.Body.Shape.BoundBox.ZMax,20)
def create_t_sketch(self):
self.Doc.getObject('Body').newObject('Sketcher::SketchObject', 'Sketch')
geo_list = [
@@ -909,5 +979,5 @@ class TestTopologicalNamingProblem(unittest.TestCase):
def tearDown(self):
""" Close our test document """
# App.closeDocument("PartDesignTestTNP")
App.closeDocument("PartDesignTestTNP")
pass