Skip to content

Commit

Permalink
Merge pull request FreeCAD#9594 from wwmayer/issue_9549_new
Browse files Browse the repository at this point in the history
 Part: fixes FreeCAD#9549: Part Fuse not working inside Part container
  • Loading branch information
sliptonic authored Jul 24, 2023
2 parents 761e807 + 0460563 commit 20768ba
Show file tree
Hide file tree
Showing 10 changed files with 262 additions and 129 deletions.
9 changes: 9 additions & 0 deletions src/App/DocumentObjectPy.xml
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,15 @@ Return -1 if element visibility is not supported or element not found, 0 if invi
in a single group, hence only a single return value.</UserDocu>
</Documentation>
</Methode>
<Methode Name="getParent">
<Documentation>
<UserDocu>Returns the group the object is in or None if it is not part of a group.
Note that an object can only be in a single group, hence only a single return
value.
The parent can be a simple group as with getParentGroup() or a
GeoFeature group as with getParentGeoFeatureGroup().</UserDocu>
</Documentation>
</Methode>
<Methode Name="getPathsByOutList">
<Documentation>
<UserDocu>Get all paths from this object to another object following the OutList.</UserDocu>
Expand Down
18 changes: 18 additions & 0 deletions src/App/DocumentObjectPyImp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,24 @@ PyObject* DocumentObjectPy::getParentGeoFeatureGroup(PyObject *args)
}
}

PyObject* DocumentObjectPy::getParent(PyObject *args)
{
if (!PyArg_ParseTuple(args, ""))
return nullptr;

try {
auto grp = getDocumentObjectPtr()->getFirstParent();
if(!grp) {
Py_INCREF(Py_None);
return Py_None;
}
return grp->getPyObject();
}
catch (const Base::Exception& e) {
throw Py::RuntimeError(e.what());
}
}

Py::Boolean DocumentObjectPy::getMustExecute() const
{
try {
Expand Down
24 changes: 24 additions & 0 deletions src/Base/Tools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,30 @@ std::string Base::Tools::escapeEncodeFilename(const std::string& s)
return result;
}

std::string Base::Tools::quoted(const char* name)
{
std::stringstream str;
str << "\"" << name << "\"";
return str.str();
}

std::string Base::Tools::quoted(const std::string& name)
{
std::stringstream str;
str << "\"" << name << "\"";
return str.str();
}

std::string Base::Tools::joinList(const std::vector<std::string>& vec,
const std::string& sep)
{
std::stringstream str;
for (const auto& it : vec) {
str << it << sep;
}
return str.str();
}

// ----------------------------------------------------------------------------

using namespace Base;
Expand Down
23 changes: 23 additions & 0 deletions src/Base/Tools.h
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,29 @@ struct BaseExport Tools
static inline QString fromStdString(const std::string & s) {
return QString::fromUtf8(s.c_str(), static_cast<int>(s.size()));
}

/**
* @brief quoted Creates a quoted string.
* @param String to be quoted.
* @return A quoted std::string.
*/
static std::string quoted(const char*);
/**
* @brief quoted Creates a quoted string.
* @param String to be quoted.
* @return A quoted std::string.
*/
static std::string quoted(const std::string&);

/**
* @brief joinList
* Join the vector of strings \a vec using the separator \a sep
* @param vec
* @param sep
* @return
*/
static std::string joinList(const std::vector<std::string>& vec,
const std::string& sep = ", ");
};


Expand Down
107 changes: 107 additions & 0 deletions src/Mod/Part/BOPTools/BOPFeatures.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# SPDX-License-Identifier: LGPL-2.1-or-later

# ***************************************************************************
# * Copyright (c) 2023 Werner Mayer <wmayer[at]users.sourceforge.net> *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, but *
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************

__title__ = "BOPTools.BOPFeatures module"
__author__ = "Werner Mayer"
__url__ = "http://www.freecad.org"
__doc__ = "Helper class to create the features for Boolean operations."

import FreeCAD
import Part

class BOPFeatures:
def __init__(self, doc):
self.doc = doc

def make_section(self, inputNames):
obj = self.doc.addObject("Part::Section", "Section")
obj.Base = self.doc.getObject(inputNames[0])
obj.Tool = self.doc.getObject(inputNames[1])
self.copy_visual_attributes(obj, obj.Base)
target = self.move_input_objects([obj.Base, obj.Tool])
if target:
target.addObject(obj)
return obj

def make_cut(self, inputNames):
obj = self.doc.addObject("Part::Cut", "Cut")
obj.Base = self.doc.getObject(inputNames[0])
obj.Tool = self.doc.getObject(inputNames[1])
self.copy_visual_attributes(obj, obj.Base)
target = self.move_input_objects([obj.Base, obj.Tool])
if target:
target.addObject(obj)
return obj

def make_common(self, inputNames):
obj = self.doc.addObject("Part::Common", "Common")
obj.Base = self.doc.getObject(inputNames[0])
obj.Tool = self.doc.getObject(inputNames[1])
self.copy_visual_attributes(obj, obj.Base)
target = self.move_input_objects([obj.Base, obj.Tool])
if target:
target.addObject(obj)
return obj

def make_multi_common(self, inputNames):
obj = self.doc.addObject("Part::MultiCommon", "Common")
obj.Shapes = [self.doc.getObject(name) for name in inputNames]
self.copy_visual_attributes(obj, obj.Shapes[0])
target = self.move_input_objects(obj.Shapes)
if target:
target.addObject(obj)
return obj

def make_fuse(self, inputNames):
obj = self.doc.addObject("Part::Fuse", "Fusion")
obj.Base = self.doc.getObject(inputNames[0])
obj.Tool = self.doc.getObject(inputNames[1])
self.copy_visual_attributes(obj, obj.Base)
target = self.move_input_objects([obj.Base, obj.Tool])
if target:
target.addObject(obj)
return obj

def make_multi_fuse(self, inputNames):
obj = self.doc.addObject("Part::MultiFuse", "Fusion")
obj.Shapes = [self.doc.getObject(name) for name in inputNames]
self.copy_visual_attributes(obj, obj.Shapes[0])
target = self.move_input_objects(obj.Shapes)
if target:
target.addObject(obj)
return obj

def move_input_objects(self, objects):
targetGroup = None
for obj in objects:
obj.Visibility = False
parent = obj.getParent()
if parent:
parent.removeObject(obj)
targetGroup = parent
return targetGroup

def copy_visual_attributes(self, target, source):
if target.ViewObject:
target.ViewObject.ShapeColor = source.ViewObject.ShapeColor
target.ViewObject.DisplayMode = source.ViewObject.DisplayMode
1 change: 1 addition & 0 deletions src/Mod/Part/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ endif(BUILD_GUI)

set(BOPTools_Scripts
BOPTools/__init__.py
BOPTools/BOPFeatures.py
BOPTools/GeneralFuseResult.py
BOPTools/JoinAPI.py
BOPTools/JoinFeatures.py
Expand Down
103 changes: 18 additions & 85 deletions src/Mod/Part/Gui/Command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ void CmdPartCut::activated(int iMsg)
}

bool askUser = false;
std::vector<std::string> names;
for (std::vector<Gui::SelectionObject>::iterator it = Sel.begin(); it != Sel.end(); ++it) {
App::DocumentObject* obj = it->getObject();
const TopoDS_Shape& shape = Part::Feature::getShape(obj);
Expand All @@ -327,34 +328,14 @@ void CmdPartCut::activated(int iMsg)
return;
askUser = true;
}
}

std::string FeatName = getUniqueObjectName("Cut");

openCommand(QT_TRANSLATE_NOOP("Command", "Part Cut"));
doCommand(Doc,"App.activeDocument().addObject(\"Part::Cut\",\"%s\")",FeatName.c_str());
doCommand(Doc,"App.activeDocument().%s.Base = App.activeDocument().%s",FeatName.c_str(),Sel[0].getFeatName());
doCommand(Doc,"App.activeDocument().%s.Tool = App.activeDocument().%s",FeatName.c_str(),Sel[1].getFeatName());

// hide the input objects and remove them from the parent group
App::DocumentObjectGroup* targetGroup = nullptr;
for (std::vector<Gui::SelectionObject>::iterator it = Sel.begin(); it != Sel.end(); ++it) {
doCommand(Gui,"Gui.activeDocument().%s.Visibility=False",it->getFeatName());
App::DocumentObjectGroup* group = it->getObject()->getGroup();
if (group) {
targetGroup = group;
doCommand(Doc, "App.activeDocument().%s.removeObject(App.activeDocument().%s)",
group->getNameInDocument(), it->getFeatName());
}
}

if (targetGroup) {
doCommand(Doc, "App.activeDocument().%s.addObject(App.activeDocument().%s)",
targetGroup->getNameInDocument(), FeatName.c_str());
names.push_back(Base::Tools::quoted(it->getFeatName()));
}

copyVisual(FeatName.c_str(), "ShapeColor", Sel[0].getFeatName());
copyVisual(FeatName.c_str(), "DisplayMode", Sel[0].getFeatName());
openCommand(QT_TRANSLATE_NOOP("Command", "Part Cut"));
doCommand(Doc, "from BOPTools import BOPFeatures");
doCommand(Doc, "bp = BOPFeatures.BOPFeatures(App.activeDocument())");
doCommand(Doc, "bp.make_cut([%s])", Base::Tools::joinList(names).c_str());
updateActive();
commitCommand();
}
Expand Down Expand Up @@ -411,11 +392,7 @@ void CmdPartCommon::activated(int iMsg)
}

bool askUser = false;
std::string FeatName = getUniqueObjectName("Common");
std::stringstream str;
std::vector<Gui::SelectionObject> partObjects;

str << "App.activeDocument()." << FeatName << ".Shapes = [";
std::vector<std::string> names;
for (std::vector<Gui::SelectionObject>::iterator it = Sel.begin(); it != Sel.end(); ++it) {
App::DocumentObject* obj = it->getObject();
const TopoDS_Shape& shape = Part::Feature::getShape(obj);
Expand All @@ -427,34 +404,14 @@ void CmdPartCommon::activated(int iMsg)
return;
askUser = true;
}
str << "App.activeDocument()." << it->getFeatName() << ",";
partObjects.push_back(*it);
}
str << "]";

openCommand(QT_TRANSLATE_NOOP("Command", "Common"));
doCommand(Doc,"App.activeDocument().addObject(\"Part::MultiCommon\",\"%s\")",FeatName.c_str());
runCommand(Doc,str.str().c_str());

// hide the input objects and remove them from the parent group
App::DocumentObjectGroup* targetGroup = nullptr;
for (std::vector<Gui::SelectionObject>::iterator it = partObjects.begin(); it != partObjects.end(); ++it) {
doCommand(Gui,"Gui.activeDocument().%s.Visibility=False",it->getFeatName());
App::DocumentObjectGroup* group = it->getObject()->getGroup();
if (group) {
targetGroup = group;
doCommand(Doc, "App.activeDocument().%s.removeObject(App.activeDocument().%s)",
group->getNameInDocument(), it->getFeatName());
}
}

if (targetGroup) {
doCommand(Doc, "App.activeDocument().%s.addObject(App.activeDocument().%s)",
targetGroup->getNameInDocument(), FeatName.c_str());
names.push_back(Base::Tools::quoted(it->getFeatName()));
}

copyVisual(FeatName.c_str(), "ShapeColor", partObjects.front().getFeatName());
copyVisual(FeatName.c_str(), "DisplayMode", partObjects.front().getFeatName());
openCommand(QT_TRANSLATE_NOOP("Command", "Common"));
doCommand(Doc, "from BOPTools import BOPFeatures");
doCommand(Doc, "bp = BOPFeatures.BOPFeatures(App.activeDocument())");
doCommand(Doc, "bp.make_multi_common([%s])", Base::Tools::joinList(names).c_str());
updateActive();
commitCommand();
}
Expand Down Expand Up @@ -511,11 +468,7 @@ void CmdPartFuse::activated(int iMsg)
}

bool askUser = false;
std::string FeatName = getUniqueObjectName("Fusion");
std::stringstream str;
std::vector<Gui::SelectionObject> partObjects;

str << "App.activeDocument()." << FeatName << ".Shapes = [";
std::vector<std::string> names;
for (std::vector<Gui::SelectionObject>::iterator it = Sel.begin(); it != Sel.end(); ++it) {
App::DocumentObject* obj = it->getObject();
const TopoDS_Shape& shape = Part::Feature::getShape(obj);
Expand All @@ -527,34 +480,14 @@ void CmdPartFuse::activated(int iMsg)
return;
askUser = true;
}
str << "App.activeDocument()." << it->getFeatName() << ",";
partObjects.push_back(*it);
}
str << "]";

openCommand(QT_TRANSLATE_NOOP("Command", "Fusion"));
doCommand(Doc,"App.activeDocument().addObject(\"Part::MultiFuse\",\"%s\")",FeatName.c_str());
runCommand(Doc,str.str().c_str());

// hide the input objects and remove them from the parent group
App::DocumentObjectGroup* targetGroup = nullptr;
for (std::vector<Gui::SelectionObject>::iterator it = partObjects.begin(); it != partObjects.end(); ++it) {
doCommand(Gui,"Gui.activeDocument().%s.Visibility=False",it->getFeatName());
App::DocumentObjectGroup* group = it->getObject()->getGroup();
if (group) {
targetGroup = group;
doCommand(Doc, "App.activeDocument().%s.removeObject(App.activeDocument().%s)",
group->getNameInDocument(), it->getFeatName());
}
names.push_back(Base::Tools::quoted(it->getFeatName()));
}

if (targetGroup) {
doCommand(Doc, "App.activeDocument().%s.addObject(App.activeDocument().%s)",
targetGroup->getNameInDocument(), FeatName.c_str());
}

copyVisual(FeatName.c_str(), "ShapeColor", partObjects.front().getFeatName());
copyVisual(FeatName.c_str(), "DisplayMode", partObjects.front().getFeatName());
openCommand(QT_TRANSLATE_NOOP("Command", "Fusion"));
doCommand(Doc, "from BOPTools import BOPFeatures");
doCommand(Doc, "bp = BOPFeatures.BOPFeatures(App.activeDocument())");
doCommand(Doc, "bp.make_multi_fuse([%s])", Base::Tools::joinList(names).c_str());
updateActive();
commitCommand();
}
Expand Down
Loading

0 comments on commit 20768ba

Please sign in to comment.