Merge branch 'main' into bgbsww-toponamingMissingAppMethods
This commit is contained in:
30
PRIVACY_POLICY.md
Normal file
30
PRIVACY_POLICY.md
Normal 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)
|
||||
|
||||
@@ -241,4 +241,4 @@ GeoFeature::getHigherElements(const char *element, bool silent) const
|
||||
return {};
|
||||
return prop->getComplexData()->getHigherElements(element, silent);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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):
|
||||
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -28,5 +28,5 @@
|
||||
using namespace std;
|
||||
|
||||
void CreateMeasureCommands() {
|
||||
Base::Console().Message("Init MeasureGui\n");
|
||||
Base::Console().Log("Init MeasureGui\n");
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user