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

30
PRIVACY_POLICY.md Normal file
View File

@@ -0,0 +1,30 @@
# FreeCAD Privacy Policy
The FreeCAD application does not collect, transmit, share or use any Personal Data.
FreeCAD is community-developed Free Software. The community does not condone the unauthorized usage of private data, so our software does not gather or send personal data.
The FreeCAD website is mostly static, it does not contain any trackers, neither ours nor third-party. The website uses cookies to remember logged in status, timezone and other
data related to navigating the site.
The website does not contain advertisements.
The software does not contain advertisements or trackers either.
## Caveats
FreeCAD is able to load or save files to/from remote servers (for some protocols and platforms). If you choose to load or save a remote file, your IP or other private data might be shared as part of the normal connection flow for the given protocol. This is out of our control and it is up to you to decide whether you trust a remote host.
The FreeCAD eco system includes user developed workbenches. These workbenches can be installed/updated using the Add-on Manager. The Add-on Manager retrieves workbenches from remote servers across the internet. Add-on workbenches are not checked for malicious content. It is your responsibility to decide whether you trust an add-on workbench.
FreeCAD is meant to manipulate CAD files which may contain metadata. It is your responsibility to verify the metadata contained in your files before you share them with others. These files may contain local directory paths which could reveal user names if the user name forms part of the path - as in “C:\MrsCAD\Documents\myFreeCADFile.FCstd”.
While running and for subsequent runs, FreeCAD uses local persistent storage for logs, configuration files, cache, thumbnails, recently accessed files and other information which may contain private data. This stays on local storage.
When reading the online version of the User Manual within FreeCAD, manual contents is requested through HTTPS connections.
FreeCAD is Free Software and therefore may be packaged by other people, who may include additional software or modify the source code. We do not vouch for these third-party packages and cannot tell you what they contain and what they do regarding your privacy. The official packages are explicitly listed in our download page.
- [based on the GIMP privacy policy](https://www.gimp.org/about/privacy.html)

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