[Part] extend SectionCutting feature for intersecting objects

- it is a well-known and often feed-backed missing feature that SectionCuttings fails for intersecting objects.
To resolve this, the objects must be put int a BooleanFragments object.
- this PR adds this functionality as option.
- since a BooleanFragments objects has a specific color, the different colors of the objects cannot be preserved. Because of this disadvantage, the BooleanFragments option will not be the default.

- this PR also modernize all for loops
This commit is contained in:
Uwe
2022-11-30 05:05:23 +01:00
parent a4b4bc99e0
commit f7df219b7b
3 changed files with 609 additions and 153 deletions

View File

@@ -110,9 +110,9 @@ SectionCut::SectionCut(QWidget* parent)
return;
}
// now store those that are currently visible
for (auto it = ObjectsList.begin(); it != ObjectsList.end(); ++it) {
if ((*it)->Visibility.getValue())
ObjectsListVisible.emplace_back(*it);
for (auto anObject : ObjectsList) {
if (anObject->Visibility.getValue())
ObjectsListVisible.emplace_back(anObject);
}
// lambda function to set color and transparency
@@ -125,8 +125,8 @@ SectionCut::SectionCut(QWidget* parent)
cutColor = vpBox->ShapeColor.getValue();
cutTransparency = vpBox->Transparency.getValue();
ui->CutColor->setColor(cutColor.asValue<QColor>());
ui->CutTransparency->setValue(cutTransparency);
ui->CutTransparency->setToolTip(QString::number(cutTransparency)
ui->CutTransparencyHS->setValue(cutTransparency);
ui->CutTransparencyHS->setToolTip(QString::number(cutTransparency)
+ QString::fromLatin1(" %"));
}
};
@@ -137,21 +137,56 @@ SectionCut::SectionCut(QWidget* parent)
Base::BoundBox3d BoundCutBox;
if (doc->getObject(BoxXName) || doc->getObject(BoxYName) || doc->getObject(BoxZName)) {
// automatic coloring must be disabled
ui->AutoCutfaceColor->setChecked(false);
ui->autoCutfaceColorCB->setChecked(false);
ui->autoBFColorCB->setChecked(false);
if (doc->getObject(CompoundName)) {
// get the object with the right name
auto compoundObject = doc->getObject(CompoundName);
Part::Compound* pcCompound = dynamic_cast<Part::Compound*>(compoundObject);
if (!pcCompound) {
Base::Console().Error(
"SectionCut error: compound is incorrectly named, cannot proceed\n");
return;
// to later store the childs
std::vector<App::DocumentObject*> compoundChilds;
// check if this is a BooleanFragments or a Part::Compound
// Part::Compound is the case when there was only one object
Part::Compound* pcCompoundPart = dynamic_cast<Part::Compound*>(compoundObject);
if (!pcCompoundPart) {
// for more security check for validity accessing its ViewProvider
auto pcCompoundBF = Gui::Application::Instance->getViewProvider(compoundObject);
if (!pcCompoundBF) {
Base::Console().Error(
"SectionCut error: compound is incorrectly named, cannot proceed\n");
return;
}
auto property = compoundObject->getPropertyByName("Shape");
Part::PropertyPartShape* objectShape =
static_cast<Part::PropertyPartShape*>(property);
BoundCompound = objectShape->getBoundingBox();
// for BooleanFragments we also need to set the checkbox, transparency and color
ui->groupBoxIntersecting->setChecked(true);
auto pcCompoundBFGO = dynamic_cast<Gui::ViewProviderGeometryObject*>(pcCompoundBF);
if (pcCompoundBFGO) {
App::Color compoundColor = pcCompoundBFGO->ShapeColor.getValue();
ui->BFragColor->setColor(compoundColor.asValue<QColor>());
int compoundTransparency = pcCompoundBFGO->Transparency.getValue();
ui->BFragTransparencyHS->setValue(compoundTransparency);
ui->BFragTransparencyHS->setToolTip(QString::number(compoundTransparency)
+ QString::fromLatin1(" %"));
// Part::Cut ignores the cutbox transparency when it is set
// to zero and the BooleanFragments transparency is not zero
// therefore limit the cutbox transparency to 1 in this case
if (compoundTransparency > 0)
ui->CutTransparencyHS->setMinimum(1);
else
ui->CutTransparencyHS->setMinimum(0);
}
compoundChilds = pcCompoundBF->claimChildren();
}
else {
BoundCompound = pcCompoundPart->Shape.getBoundingBox();
pcCompoundPart->Links.getLinks(compoundChilds);
}
BoundCompound = pcCompound->Shape.getBoundingBox();
// make parent objects of links visible to handle the case that
// the cutting is started when only an existing cut was visible
std::vector<App::DocumentObject*> compoundObjects;
pcCompound->Links.getLinks(compoundObjects);
for (auto aCompoundObj : compoundObjects) {
for (auto aCompoundObj : compoundChilds) {
App::Link* pcLink = dynamic_cast<App::Link*>(aCompoundObj);
auto LinkedObject = pcLink ? pcLink->getLink() : nullptr;
if (LinkedObject) {
@@ -273,9 +308,17 @@ SectionCut::SectionCut(QWidget* parent)
connect(ui->flipZ, &QPushButton::clicked, this, &SectionCut::onFlipZclicked);
connect(ui->RefreshCutPB, &QPushButton::clicked, this, &SectionCut::onRefreshCutPBclicked);
connect(ui->CutColor, &QPushButton::clicked, this, &SectionCut::onCutColorclicked);
connect(ui->CutTransparency, &QSlider::sliderMoved, this,
&SectionCut::onTransparencySliderMoved);
connect(ui->CutTransparency, &QSlider::valueChanged, this, &SectionCut::onTransparencyChanged);
connect(ui->CutTransparencyHS, &QSlider::sliderMoved,
this, &SectionCut::onTransparencyHSMoved);
connect(ui->CutTransparencyHS, &QSlider::valueChanged,
this, &SectionCut::onTransparencyHSChanged);
connect(ui->groupBoxIntersecting, &QGroupBox::toggled,
this, &SectionCut::onGroupBoxIntersectingToggled);
connect(ui->BFragColor, &QPushButton::clicked, this, &SectionCut::onBFragColorclicked);
connect(ui->BFragTransparencyHS, &QSlider::sliderMoved,
this, &SectionCut::onBFragTransparencyHSMoved);
connect(ui->BFragTransparencyHS,&QSlider::valueChanged,
this, &SectionCut::onBFragTransparencyHSChanged);
// if there is a cut, perform it
if (hasBoxX || hasBoxY || hasBoxZ) {
@@ -314,7 +357,6 @@ void SectionCut::startCutting(bool isInitial)
onRefreshCutPBclicked();
App::DocumentObject* anObject = nullptr;
std::vector<App::DocumentObjectT>::iterator it;
// lambda function to delete objects
auto deleteObject = [&](const char* objectName) {
@@ -338,7 +380,7 @@ void SectionCut::startCutting(bool isInitial)
compoundTransparency = CompoundVP->Transparency.getValue();
}
};
// delete the objects we might have already created to cut
// we must do this because we support several cuts at once and
// it is dangerous to deal with the fact that the user is free
@@ -364,16 +406,28 @@ void SectionCut::startCutting(bool isInitial)
if (doc->getObject(BoxXName))
deleteObject(BoxXName);
if (doc->getObject(CompoundName)) {
// get the object with the right name
auto compoundObject = doc->getObject(CompoundName);
Part::Compound* pcCompoundDel = dynamic_cast<Part::Compound*>(compoundObject);
if (!pcCompoundDel) {
Base::Console().Error(
"SectionCut error: compound is incorrectly named, cannot proceed\n");
return;
// to later store the childs
std::vector<App::DocumentObject*> compoundChilds;
// check if this is a BooleanFragments or a Part::Compound
Part::Compound* pcCompoundDelPart = dynamic_cast<Part::Compound*>(compoundObject);
Gui::ViewProvider* pcCompoundDelBF;
if (!pcCompoundDelPart) {
// check for BooleanFragments
pcCompoundDelBF = Gui::Application::Instance->getViewProvider(compoundObject);
if (!pcCompoundDelBF) {
Base::Console().Error(
"SectionCut error: compound is incorrectly named, cannot proceed\n");
return;
}
compoundChilds = pcCompoundDelBF->claimChildren();
}
// get the compound content
std::vector<App::DocumentObject*> compoundObjects;
pcCompoundDel->Links.getLinks(compoundObjects);
else {
pcCompoundDelPart->Links.getLinks(compoundChilds);
}
// first delete the compound
auto foundObj = std::find_if(
ObjectsListVisible.begin(), ObjectsListVisible.end(),
@@ -383,24 +437,23 @@ void SectionCut::startCutting(bool isInitial)
ObjectsListVisible.erase(foundObj);
doc->removeObject(CompoundName);
// now delete the objects that have been part of the compound
for (auto itCompound = compoundObjects.begin(); itCompound != compoundObjects.end();
itCompound++) {
anObject = doc->getObject((*itCompound)->getNameInDocument());
for (auto aChild : compoundChilds) {
anObject = doc->getObject(aChild->getNameInDocument());
auto foundObjInner = std::find_if(ObjectsListVisible.begin(), ObjectsListVisible.end(),
[anObject](const App::DocumentObjectT &objInner) {
return (objInner.getObject() == anObject);
});
if (foundObjInner != ObjectsListVisible.end())
ObjectsListVisible.erase((foundObjInner));
doc->removeObject((*itCompound)->getNameInDocument());
doc->removeObject(aChild->getNameInDocument());
}
}
// make all objects visible that have been visible when the dialog was called
// because we made them invisible when we created cuts
for (it = ObjectsListVisible.begin(); it != ObjectsListVisible.end(); ++it) {
if (it->getObject()) // a formerly visible object might have been deleted
it->getObject()->Visibility.setValue(true);
for (auto& aVisObject : ObjectsListVisible) {
if (aVisObject.getObject()) // a formerly visible object might have been deleted
aVisObject.getObject()->Visibility.setValue(true);
else {
// we must refresh the ObjectsListVisible list
onRefreshCutPBclicked();
@@ -416,21 +469,20 @@ void SectionCut::startCutting(bool isInitial)
// those that have a solid shape
std::vector<App::DocumentObject*> ObjectsListCut;
bool isLinkAssembly = false;
for (it = ObjectsListVisible.begin(); it != ObjectsListVisible.end(); ++it) {
App::DocumentObject* object = it->getObject();
for (auto& aVisObject : ObjectsListVisible) {
App::DocumentObject* object = aVisObject.getObject();
if (!object) {
continue;
}
// we need all Link objects in App::Part for example for Assembly 4
if (object->getTypeId() == Base::Type::fromName("App::Part")) {
App::Part* pcPart = static_cast<App::Part*>(object);
// collect all its link objects
auto groupObjects = pcPart->Group.getValue();
for (auto itGO = groupObjects.begin(); itGO != groupObjects.end(); ++itGO) {
if ((*itGO)->getTypeId() == Base::Type::fromName("App::Link")) {
ObjectsListCut.push_back((*itGO));
for (auto aGroupObject : groupObjects) {
if (aGroupObject->getTypeId() == Base::Type::fromName("App::Link")) {
ObjectsListCut.push_back(aGroupObject);
// we assume that App::Links inside a App::Part are an assembly
isLinkAssembly = true;
}
@@ -438,12 +490,14 @@ void SectionCut::startCutting(bool isInitial)
}
// get all shapes that are also Part::Features
if (object->getPropertyByName("Shape")
&& object->getTypeId().isDerivedFrom(Base::Type::fromName("Part::Feature"))) {
&& object->getTypeId().isDerivedFrom(
Base::Type::fromName("Part::Feature"))) {
// sort out 2D objects, datums, App:Parts, compounds and objects that are
// part of a PartDesign body
if (!object->getTypeId().isDerivedFrom(
Base::Type::fromName("Part::Part2DObject"))
&& !object->getTypeId().isDerivedFrom(Base::Type::fromName("Part::Datum"))
&& !object->getTypeId().isDerivedFrom(
Base::Type::fromName("Part::Datum"))
&& !object->getTypeId().isDerivedFrom(
Base::Type::fromName("PartDesign::Feature"))
&& !object->getTypeId().isDerivedFrom(
@@ -474,29 +528,30 @@ void SectionCut::startCutting(bool isInitial)
// sort out objects that are part of Part::Boolean, Part::MultiCommon, Part::MultiFuse,
// Part::Thickness and Part::FilletBase
std::vector<App::DocumentObject*>::iterator it2;
std::vector<App::DocumentObject*>::iterator it3;
// check list of visible objects and not cut list because we want to repove from the cut list
for (it = ObjectsListVisible.begin(); it != ObjectsListVisible.end(); ++it) {
App::DocumentObject* object = it->getObject();
// check list of visible objects and not cut list because we want to remove from the cut list
for (auto &aVisObject : ObjectsListVisible) {
App::DocumentObject* object = aVisObject.getObject();
if (!object) {
continue;
}
if (object->getTypeId().isDerivedFrom(Base::Type::fromName("Part::Boolean"))
|| object->getTypeId().isDerivedFrom(Base::Type::fromName("Part::MultiCommon"))
|| object->getTypeId().isDerivedFrom(Base::Type::fromName("Part::MultiFuse"))
|| object->getTypeId().isDerivedFrom(Base::Type::fromName("Part::Thickness"))
|| object->getTypeId().isDerivedFrom(
Base::Type::fromName("Part::MultiCommon"))
|| object->getTypeId().isDerivedFrom(
Base::Type::fromName("Part::MultiFuse"))
|| object->getTypeId().isDerivedFrom(
Base::Type::fromName("Part::Thickness"))
|| object->getTypeId().isDerivedFrom(
Base::Type::fromName("Part::FilletBase"))) {
// get possible links
auto subObjectList = object->getOutList();
// if there are links, delete them
if (!subObjectList.empty()) {
for (it2 = subObjectList.begin(); it2 != subObjectList.end(); ++it2) {
for (it3 = ObjectsListCut.begin(); it3 != ObjectsListCut.end(); ++it3) {
if ((*it2) == (*it3)) {
ObjectsListCut.erase(it3);
for (auto aSubObj : subObjectList) {
for (auto itCutObj = ObjectsListCut.begin(); itCutObj != ObjectsListCut.end();
++itCutObj) {
if (aSubObj == *itCutObj) {
ObjectsListCut.erase(itCutObj);
break;
}
}
@@ -526,8 +581,11 @@ void SectionCut::startCutting(bool isInitial)
return;
}
// disable intersection option because BooleanFragments requires at least 2 objects
ui->groupBoxIntersecting->setEnabled(ObjectsListCut.size() > 1);
// we cut this way:
// 1. put all existing objects into a part compound
// 1. put all existing objects into a either a Part::Compound or a BooleanFragments object
// 2. create a box with the size of the bounding box
// 3. cut the box from the compound
@@ -543,14 +601,6 @@ void SectionCut::startCutting(bool isInitial)
// disable refresh button
ui->RefreshCutPB->setEnabled(false);
// create an empty compound
auto CutCompound = doc->addObject("Part::Compound", CompoundName);
if (!CutCompound) {
Base::Console().Error( (std::string("SectionCut error: ")
+ std::string(CompoundName) + std::string(" could not be added\n")).c_str() );
return;
}
Part::Compound* pcCompound = static_cast<Part::Compound*>(CutCompound);
// store color and transparency of first object
App::Color cutColor;
int cutTransparency;
@@ -562,17 +612,17 @@ void SectionCut::startCutting(bool isInitial)
cutColor = vpFirstObject->ShapeColor.getValue();
cutTransparency = vpFirstObject->Transparency.getValue();
}
// fill it with all found elements with the copies of the elements
int count = 0;
for (auto itCuts = ObjectsListCut.begin(); itCuts != ObjectsListCut.end(); ++itCuts, count++) {
// create link objects for all found elements
std::vector<App::DocumentObject*> ObjectsListLinks;
for (auto itCuts : ObjectsListCut) {
// first create a link with a unique name
std::string newName;
// since links to normal Part objects all have the document name "Link",
// use their label text instead
if ((*itCuts)->getTypeId() == Base::Type::fromName("App::Link"))
newName = (*itCuts)->Label.getValue();
if (itCuts->getTypeId() == Base::Type::fromName("App::Link"))
newName = itCuts->Label.getValue();
else
newName = (*itCuts)->getNameInDocument();
newName = itCuts->getNameInDocument();
newName = newName + "_CutLink";
auto newObject = doc->addObject("App::Link", newName.c_str());
@@ -582,17 +632,20 @@ void SectionCut::startCutting(bool isInitial)
}
App::Link* pcLink = static_cast<App::Link*>(newObject);
// set the object to the created empty link object
pcLink->LinkedObject.setValue((*itCuts));
pcLink->LinkedObject.setValue(itCuts);
// we want to get the link at the same position as the original
pcLink->LinkTransform.setValue(true);
// add link to list to later add this to the compound object
ObjectsListLinks.push_back(newObject);
// if the object is part of an App::Part container,
// the link needs to get the container placement
auto parents = (*itCuts)->getInList();
auto parents = itCuts->getInList();
if (!parents.empty()) {
for (auto itParents = parents.begin(); itParents != parents.end(); ++itParents) {
if ((*itParents)->getTypeId() == Base::Type::fromName("App::Part")) {
App::Part* pcPartParent = static_cast<App::Part*>((*itParents));
for (auto parent : parents) {
if (parent->getTypeId() == Base::Type::fromName("App::Part")) {
App::Part* pcPartParent = static_cast<App::Part*>(parent);
auto placement = Base::freecad_dynamic_cast<App::PropertyPlacement>(
pcPartParent->getPropertyByName("Placement"));
if (placement)
@@ -601,16 +654,13 @@ void SectionCut::startCutting(bool isInitial)
}
}
// add the link to the compound
pcCompound->Links.set1Value(count, newObject);
// hide the objects since only the cut should later be visible
(*itCuts)->Visibility.setValue(false);
itCuts->Visibility.setValue(false);
// check if all objects have same color and transparency
if (ui->AutoCutfaceColor->isChecked()) {
if (ui->autoCutfaceColorCB->isChecked() || ui->autoBFColorCB->isChecked()) {
auto vpObject = dynamic_cast<Gui::ViewProviderGeometryObject*>(
Gui::Application::Instance->getViewProvider(*itCuts));
Gui::Application::Instance->getViewProvider(itCuts));
if (vpObject) {
if (cutColor != vpObject->ShapeColor.getValue())
autoColor = false;
@@ -620,24 +670,67 @@ void SectionCut::startCutting(bool isInitial)
}
}
// set transparency for the compound
// if there was no compound, take the setting for the cut face
if (compoundTransparency == -1)
compoundTransparency = ui->CutTransparency->value();
auto vpCompound = dynamic_cast<Gui::ViewProviderGeometryObject*>(
Gui::Application::Instance->getViewProvider(pcCompound));
if (vpCompound)
vpCompound->Transparency.setValue(compoundTransparency);
App::DocumentObject* CutCompoundBF = nullptr;
Part::Compound* CutCompoundPart = nullptr;
App::DocumentObject* CutCompoundPartObj = nullptr;
// compute the filled compound
pcCompound->recomputeFeature();
// specify transparency for the compound
// if there was no compound, take the setting for the cut face
if (ui->groupBoxIntersecting->isChecked())
compoundTransparency = ui->BFragTransparencyHS->value();
if (compoundTransparency == -1)
compoundTransparency = ui->CutTransparencyHS->value();
// create BooleanFragments and fill it
if (ui->groupBoxIntersecting->isChecked() && ObjectsListCut.size() > 1) {
CutCompoundBF = CreateBooleanFragments(doc);
// the BooleanFragment implementation requires to first add at least 2 objects
// before any other setting to the BooleanFragment object can be made
App::PropertyLinkList* CutLinkList =
dynamic_cast<App::PropertyLinkList*>(CutCompoundBF->getPropertyByName("Objects"));
if (!CutCompoundBF) {
Base::Console().Error((std::string("SectionCut error: ") + std::string(CompoundName)
+ std::string(" could not be added\n")).c_str());
return;
}
CutLinkList->setValue(ObjectsListLinks);
// make all objects in the BooleanFragments object invisible to later only show the cut
for (auto aLinkObj : ObjectsListLinks) {
aLinkObj->Visibility.setValue(false);
}
// set the transparency
auto vpCompound = dynamic_cast<Gui::ViewProviderGeometryObject*>(
Gui::Application::Instance->getViewProvider(CutCompoundBF));
vpCompound->Transparency.setValue(compoundTransparency);
// set the color
// setBooleanFragmentsColor also does a non-recursive recompute
setBooleanFragmentsColor();
}
else { // create Part::Compound and fill it
// if there is only one object to be cut, we cannot create a BooleanFragments object
CutCompoundPartObj = doc->addObject("Part::Compound", CompoundName);
if (!CutCompoundPartObj) {
Base::Console().Error((std::string("SectionCut error: ") + std::string(CompoundName)
+ std::string(" could not be added\n")).c_str());
return;
}
CutCompoundPart = static_cast<Part::Compound*>(CutCompoundPartObj);
// add the link to the compound
CutCompoundPart->Links.setValue(ObjectsListLinks);
// set the transparency
auto vpCompound = dynamic_cast<Gui::ViewProviderGeometryObject*>(
Gui::Application::Instance->getViewProvider(CutCompoundPartObj));
vpCompound->Transparency.setValue(compoundTransparency);
CutCompoundPart->recomputeFeature();
}
// make all objects invisible so that only the compound remains
for (it = ObjectsListVisible.begin(); it != ObjectsListVisible.end(); ++it) {
App::DocumentObject* object = it->getObject();
for (auto &aVisObject : ObjectsListVisible) {
App::DocumentObject* object = aVisObject.getObject();
if (object) {
object->Visibility.setValue(false);
}
object->Visibility.setValue(false);
}
// the area in which we can cut is the size of the compound
@@ -673,25 +766,39 @@ void SectionCut::startCutting(bool isInitial)
hasBoxCustom = false;
// if automatic, we take this color for the cut
if (ui->AutoCutfaceColor->isChecked()) {
if (autoColor) {
if (autoColor) {
if (ui->autoCutfaceColorCB->isChecked()) {
ui->CutColor->blockSignals(true);
ui->CutColor->setColor(cutColor.asValue<QColor>());
ui->CutColor->blockSignals(false);
}
if (autoTransparency) {
ui->CutTransparency->blockSignals(true);
ui->CutTransparency->setValue(cutTransparency);
ui->CutTransparency->setToolTip(QString::number(cutTransparency)
+ QString::fromLatin1(" %"));
ui->CutTransparency->blockSignals(false);
if (ui->autoBFColorCB->isChecked()) {
ui->BFragColor->blockSignals(true);
ui->BFragColor->setColor(cutColor.asValue<QColor>());
ui->BFragColor->blockSignals(false);
}
}
if (autoTransparency) {
if (ui->autoCutfaceColorCB->isChecked()) {
ui->CutTransparencyHS->blockSignals(true);
ui->CutTransparencyHS->setValue(cutTransparency);
ui->CutTransparencyHS->setToolTip(QString::number(cutTransparency)
+ QString::fromLatin1(" %"));
ui->CutTransparencyHS->blockSignals(false);
}
if (ui->autoBFColorCB->isChecked()) {
ui->BFragTransparencyHS->blockSignals(true);
ui->BFragTransparencyHS->setValue(cutTransparency);
ui->BFragTransparencyHS->setToolTip(QString::number(cutTransparency)
+ QString::fromLatin1(" %"));
ui->BFragTransparencyHS->blockSignals(false);
}
}
// read cutface color for the cut box
App::Color boxColor;
boxColor.setValue<QColor>(ui->CutColor->color());
int boxTransparency = ui->CutTransparency->value();
int boxTransparency = ui->CutTransparencyHS->value();
// lambda function to set placement, shape color and transparency
auto setPlaceColorTransparency = [&](Part::Box* pcBox) {
@@ -737,16 +844,16 @@ void SectionCut::startCutting(bool isInitial)
}
// we don't set the value to ui->cutX because this would refresh the cut
// which we don't have yet, thus do this later
//set the box position
// set the box position
if (!ui->flipX->isChecked())
BoxOriginSet.x = CutPosX - (BoundingBoxSize[0] + 1.0);
else //flipped
else // flipped
BoxOriginSet.x = CutPosX;
// we made the box 1.0 larger that we can place it 0.5 below the bounding box
BoxOriginSet.y = BoundingBoxOrigin[1] - 0.5;
BoxOriginSet.z = BoundingBoxOrigin[2] - 0.5;
placement.setPosition(BoxOriginSet);
// set box color
// set box placement, color and transparency
setPlaceColorTransparency(pcBox);
// create a cut feature
@@ -757,7 +864,10 @@ void SectionCut::startCutting(bool isInitial)
return;
}
Part::Cut* pcCut = static_cast<Part::Cut*>(CutFeature);
pcCut->Base.setValue(CutCompound);
if (ObjectsListCut.size() == 1 || !(ui->groupBoxIntersecting->isChecked()))
pcCut->Base.setValue(CutCompoundPart);
else
pcCut->Base.setValue(CutCompoundBF);
pcCut->Tool.setValue(CutBox);
// we must set the compoundTransparency also for the cut
setTransparency(pcCut);
@@ -800,11 +910,11 @@ void SectionCut::startCutting(bool isInitial)
else if (CutPosY <= ui->cutY->minimum()) {
CutPosY = ui->cutY->minimum() + 0.1; // short above the minimum
}
//set the box position
// set the box position
BoxOriginSet.x = BoundingBoxOrigin[0] - 0.5;
if (!ui->flipY->isChecked())
BoxOriginSet.y = CutPosY - (BoundingBoxSize[1] + 1.0);
else //flipped
else // flipped
BoxOriginSet.y = CutPosY;
BoxOriginSet.z = BoundingBoxOrigin[2] - 0.5;
placement.setPosition(BoxOriginSet);
@@ -818,10 +928,15 @@ void SectionCut::startCutting(bool isInitial)
}
Part::Cut* pcCut = static_cast<Part::Cut*>(CutFeature);
// if there is already a cut, we must take it as feature to be cut
if (hasBoxX)
if (hasBoxX) {
pcCut->Base.setValue(doc->getObject(CutXName));
else
pcCut->Base.setValue(CutCompound);
}
else {
if (ObjectsListCut.size() == 1 || !(ui->groupBoxIntersecting->isChecked()))
pcCut->Base.setValue(CutCompoundPart);
else
pcCut->Base.setValue(CutCompoundBF);
}
pcCut->Tool.setValue(CutBox);
setTransparency(pcCut);
@@ -857,12 +972,12 @@ void SectionCut::startCutting(bool isInitial)
else if (CutPosZ <= ui->cutZ->minimum()) {
CutPosZ = ui->cutZ->minimum() + 0.1; // short above the minimum
}
//set the box position
// set the box position
BoxOriginSet.x = BoundingBoxOrigin[0] - 0.5;
BoxOriginSet.y = BoundingBoxOrigin[1] - 0.5;
if (!ui->flipY->isChecked())
BoxOriginSet.z = CutPosZ - (BoundingBoxSize[2] + 1.0);
else //flipped
else // flipped
BoxOriginSet.z = CutPosZ;
placement.setPosition(BoxOriginSet);
setPlaceColorTransparency(pcBox);
@@ -882,7 +997,10 @@ void SectionCut::startCutting(bool isInitial)
pcCut->Base.setValue(doc->getObject(CutXName));
}
else {
pcCut->Base.setValue(CutCompound);
if (ObjectsListCut.size() == 1 || !(ui->groupBoxIntersecting->isChecked()))
pcCut->Base.setValue(CutCompoundPart);
else
pcCut->Base.setValue(CutCompoundBF);
}
pcCut->Tool.setValue(CutBox);
setTransparency(pcCut);
@@ -920,9 +1038,9 @@ SectionCut::~SectionCut()
if (!ui->keepOnlyCutCB->isChecked()) {
// make all objects visible that have been visible when the dialog was called
// because we made them invisible when we created cuts
for (auto it = ObjectsListVisible.begin(); it != ObjectsListVisible.end(); ++it) {
if (it->getObject()) // a formerly visible object might have been deleted
it->getObject()->Visibility.setValue(true);
for (auto& aVisObj : ObjectsListVisible) {
if (aVisObj.getObject())// a formerly visible object might have been deleted
aVisObj.getObject()->Visibility.setValue(true);
}
}
}
@@ -1590,8 +1708,9 @@ void SectionCut::onFlipZclicked()
// changes the cutface color
void SectionCut::onCutColorclicked()
{
if (ui->groupBoxX->isChecked() || ui->groupBoxY->isChecked() || ui->groupBoxZ->isChecked())
if (ui->groupBoxX->isChecked() || ui->groupBoxY->isChecked() || ui->groupBoxZ->isChecked()) {
changeCutBoxColors();
}
}
// changes cutbox colors
@@ -1605,38 +1724,173 @@ void SectionCut::changeCutBoxColors()
App::Color boxColor;
boxColor.setValue<QColor>(ui->CutColor->color());
boxVPGO->ShapeColor.setValue(boxColor);
int boxTransparency = ui->CutTransparency->value();
int boxTransparency = ui->CutTransparencyHS->value();
boxVPGO->Transparency.setValue(boxTransparency);
}
};
if (doc->getObject(BoxXName))
if (doc->getObject(BoxXName)) {
setColorTransparency(doc->getObject(BoxXName));
if (doc->getObject(BoxYName))
}
if (doc->getObject(BoxYName)) {
setColorTransparency(doc->getObject(BoxYName));
if (doc->getObject(BoxZName))
}
if (doc->getObject(BoxZName)) {
setColorTransparency(doc->getObject(BoxZName));
}
// we must recompute the topmost cut to make the color visible
if (doc->getObject(CutZName))
doc->getObject(CutZName)->recomputeFeature(false);
else if (doc->getObject(CutYName))
doc->getObject(CutYName)->recomputeFeature(false);
else if (doc->getObject(CutXName))
// we must hereby first recompute ewvery cut non-recursively in the order X -> Y -> Z
// eventually recompute the topmost cut recursively
if (doc->getObject(CutXName)) {
doc->getObject(CutXName)->recomputeFeature(false);
}
if (doc->getObject(CutYName)) {
doc->getObject(CutYName)->recomputeFeature(false);
}
if (doc->getObject(CutZName)) {
doc->getObject(CutZName)->recomputeFeature(false);
}
if (doc->getObject(CutZName)) {
doc->getObject(CutZName)->recomputeFeature(true);
}
else if (doc->getObject(CutYName)) {
doc->getObject(CutYName)->recomputeFeature(true);
}
else if (doc->getObject(CutXName)) {
doc->getObject(CutXName)->recomputeFeature(true);
}
}
void SectionCut::onTransparencySliderMoved(int val)
void SectionCut::onTransparencyHSMoved(int val)
{
ui->CutTransparency->setToolTip(QString::number(val) + QString::fromLatin1(" %"));
ui->CutTransparencyHS->setToolTip(QString::number(val) + QString::fromLatin1(" %"));
// highlight the tooltip
QToolTip::showText(QCursor::pos(), QString::number(val) + QString::fromLatin1(" %"), nullptr);
if (ui->groupBoxX->isChecked() || ui->groupBoxY->isChecked() || ui->groupBoxZ->isChecked())
if (ui->groupBoxX->isChecked() || ui->groupBoxY->isChecked() || ui->groupBoxZ->isChecked()) {
changeCutBoxColors();
}
}
void SectionCut::onTransparencyChanged(int val)
void SectionCut::onTransparencyHSChanged(int val)
{
onTransparencySliderMoved(val);
onTransparencyHSMoved(val);
}
// change from/to BooleanFragments compound
void SectionCut::onGroupBoxIntersectingToggled()
{
// re-cut
if (ui->groupBoxX->isChecked() || ui->groupBoxY->isChecked() || ui->groupBoxZ->isChecked()) {
startCutting();
}
}
// changes the BooleanFragments color
void SectionCut::onBFragColorclicked()
{
// when there is no cut yet, there is nothing to do
if (!(ui->groupBoxX->isChecked() || ui->groupBoxY->isChecked() || ui->groupBoxZ->isChecked())) {
return;
}
setBooleanFragmentsColor();
// we must recompute the topmost cut to make the color visible
if (doc->getObject(CutZName)) {
doc->getObject(CutZName)->recomputeFeature(true);
}
else if (doc->getObject(CutYName)) {
doc->getObject(CutYName)->recomputeFeature(true);
}
else if (doc->getObject(CutXName)) {
doc->getObject(CutXName)->recomputeFeature(true);
}
}
// sets BooleanFragments color
void SectionCut::setBooleanFragmentsColor()
{
App::DocumentObject* compoundObject;
if (doc->getObject(CompoundName)) {
// get the object with the right name
compoundObject = doc->getObject(CompoundName);
}
else {
Base::Console().Error("SectionCut error: compound is incorrectly named, cannot proceed\n");
return;
}
// assure it is not a Part::Compound
Part::Compound* pcCompound = dynamic_cast<Part::Compound*>(compoundObject);
Gui::ViewProvider* CompoundBFVP;
if (!pcCompound) {
// check for valid BooleanFragments by accessing its ViewProvider
CompoundBFVP = Gui::Application::Instance->getViewProvider(compoundObject);
if (!CompoundBFVP) {
Base::Console().Error("SectionCut error: cannot access ViewProvider of cut compound\n");
return;
}
auto CutCompoundBFGeom = dynamic_cast<Gui::ViewProviderGeometryObject*>(CompoundBFVP);
if (CutCompoundBFGeom) {
App::Color BFColor;
BFColor.setValue<QColor>(ui->BFragColor->color());
CutCompoundBFGeom->ShapeColor.setValue(BFColor);
int BFTransparency = ui->BFragTransparencyHS->value();
CutCompoundBFGeom->Transparency.setValue(BFTransparency);
compoundObject->recomputeFeature(false);
}
}
}
void SectionCut::onBFragTransparencyHSMoved(int val)
{
// lambda to set transparency
auto setTransparency = [&](App::DocumentObject* cutObject) {
Gui::ViewProvider* CutVP = Gui::Application::Instance->getViewProvider(cutObject);
if (!CutVP) {
Base::Console().Error(
"SectionCut error: cannot access ViewProvider of cut object\n");
return;
}
auto CutVPGeom = dynamic_cast<Gui::ViewProviderGeometryObject*>(CutVP);
if (CutVPGeom) {
int BFTransparency = ui->BFragTransparencyHS->value();
CutVPGeom->Transparency.setValue(BFTransparency);
cutObject->recomputeFeature(true);
}
};
// Part::Cut ignores the cutbox transparency when it is set
// to zero and the BooleanFragments transparency is not zero
// therefore limit the cutbox transparency to 1 in this case
if (val > 0) {
ui->CutTransparencyHS->setMinimum(1);
}
else {
ui->CutTransparencyHS->setMinimum(0);
}
ui->BFragTransparencyHS->setToolTip(QString::number(val) + QString::fromLatin1(" %"));
// highlight the tooltip
QToolTip::showText(QCursor::pos(), QString::number(val) + QString::fromLatin1(" %"), nullptr);
// when there is no cut yet, there is nothing else to do
if (ui->groupBoxX->isChecked() || ui->groupBoxY->isChecked() || ui->groupBoxZ->isChecked()) {
setBooleanFragmentsColor();
// we must set the transparency to every cut and recompute in the order X -> Y -> Z
if (doc->getObject(CutXName)) {
setTransparency(doc->getObject(CutXName));
}
if (doc->getObject(CutYName)) {
setTransparency(doc->getObject(CutYName));
}
if (doc->getObject(CutZName)) {
setTransparency(doc->getObject(CutZName));
}
}
}
void SectionCut::onBFragTransparencyHSChanged(int val)
{
onBFragTransparencyHSMoved(val);
}
// refreshes the list of document objects and the visible objects
@@ -1658,11 +1912,13 @@ void SectionCut::onRefreshCutPBclicked()
// empty the ObjectsListVisible
ObjectsListVisible.clear();
// now store those that are currently visible
for (auto it = ObjectsList.begin(); it != ObjectsList.end(); ++it) {
if ((*it)->Visibility.getValue()) {
ObjectsListVisible.emplace_back(*it);
for (auto anObject : ObjectsList) {
if (anObject->Visibility.getValue()) {
ObjectsListVisible.emplace_back(anObject);
}
}
// disable intersection option because BooleanFragments requires at least 2 objects
ui->groupBoxIntersecting->setEnabled(ObjectsListVisible.size() > 1);
// reset defaults
hasBoxX = false;
hasBoxY = false;
@@ -1776,4 +2032,22 @@ void SectionCut::refreshCutRanges(SbBox3f BoundingBox,
}
}
App::DocumentObject* SectionCut::CreateBooleanFragments(App::Document* doc)
{
// create the object
Gui::Command::doCommand(Gui::Command::Doc, "import FreeCAD");
Gui::Command::doCommand(Gui::Command::Doc, "from BOPTools import SplitFeatures");
Gui::Command::doCommand(Gui::Command::Doc,
"SplitFeatures.makeBooleanFragments(name=\"%s\")",
CompoundName);
// check for success
App::DocumentObject* object = doc->getObject(CompoundName);
if (!object) {
Base::Console().Error((std::string("SectionCut error: ") + std::string(CompoundName)
+ std::string(" could not be added\n")).c_str());
return nullptr;
}
return object;
}
#include "moc_SectionCutting.cpp"

View File

@@ -61,14 +61,18 @@ protected Q_SLOTS:
void onFlipZclicked();
void onRefreshCutPBclicked();
void onCutColorclicked();
void onTransparencySliderMoved(int);
void onTransparencyChanged(int);
void onTransparencyHSMoved(int);
void onTransparencyHSChanged(int);
void onGroupBoxIntersectingToggled();
void onBFragColorclicked();
void onBFragTransparencyHSMoved(int);
void onBFragTransparencyHSChanged(int);
public:
void reject() override;
private:
Ui_SectionCut* ui;
std::unique_ptr<Ui_SectionCut> ui;
std::vector<App::DocumentObjectT> ObjectsListVisible;
App::Document* doc = nullptr; // pointer to active document
bool hasBoxX = false;
@@ -90,6 +94,8 @@ private:
const char* CutYName = "SectionCutY";
const char* CutZName = "SectionCutZ";
void changeCutBoxColors();
App::DocumentObject* CreateBooleanFragments(App::Document* doc);
void setBooleanFragmentsColor();
};
} // namespace PartGui

View File

@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>230</width>
<height>427</height>
<height>514</height>
</rect>
</property>
<property name="maximumSize">
@@ -19,7 +19,7 @@
<property name="windowTitle">
<string>Permanent Section Cutting</string>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<layout class="QGridLayout" name="gridLayout_6">
<item row="0" column="0" colspan="3">
<widget class="QGroupBox" name="groupBoxX">
<property name="maximumSize">
@@ -275,7 +275,7 @@
<height>16777215</height>
</size>
</property>
<property name="color" stdset="0">
<property name="color">
<color>
<red>203</red>
<green>203</green>
@@ -291,7 +291,7 @@
</widget>
</item>
<item row="0" column="2">
<widget class="Gui::PrefCheckBox" name="AutoCutfaceColor">
<widget class="Gui::PrefCheckBox" name="autoCutfaceColorCB">
<property name="minimumSize">
<size>
<width>0</width>
@@ -301,7 +301,8 @@
<property name="toolTip">
<string>If checked, the color and transparency
will be taken from the cut objects.
Works only if all objects have the same values.</string>
Works only properly if all objects
have the same values.</string>
</property>
<property name="text">
<string>Auto</string>
@@ -334,7 +335,7 @@ Works only if all objects have the same values.</string>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="Gui::PrefSlider" name="CutTransparency">
<widget class="Gui::PrefSlider" name="CutTransparencyHS">
<property name="enabled">
<bool>false</bool>
</property>
@@ -358,7 +359,150 @@ Works only if all objects have the same values.</string>
</layout>
</widget>
</item>
<item row="4" column="0">
<item row="4" column="0" colspan="3">
<widget class="QGroupBox" name="groupBoxIntersecting">
<property name="maximumSize">
<size>
<width>215</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Allows to cut objects intersecting each other
for the price that all cut objects
will get the same color</string>
</property>
<property name="title">
<string>Cut intersecting objects</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Color of cut face</string>
</property>
<property name="text">
<string>Color</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="Gui::PrefColorButton" name="BFragColor">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>50</width>
<height>20</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Color for all objects</string>
</property>
<property name="color">
<color>
<red>203</red>
<green>203</green>
<blue>203</blue>
</color>
</property>
<property name="prefEntry" stdset="0">
<cstring>DefaultShapeColor</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>View</cstring>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="Gui::PrefCheckBox" name="autoBFColorCB">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>If checked, the color and transparency
will be taken from the cut objects.
Works only properly if all objects
have the same values.</string>
</property>
<property name="text">
<string>Auto</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>TwoSideRendering</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Part</cstring>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_8">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Transparency of cut face</string>
</property>
<property name="text">
<string>Transparency</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="Gui::PrefSlider" name="BFragTransparencyHS">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string notr="true">0 %</string>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="prefEntry" stdset="0">
<cstring>DefaultShapeTransparency</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>View</cstring>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="5" column="0">
<widget class="QPushButton" name="RefreshCutPB">
<property name="enabled">
<bool>true</bool>
@@ -371,7 +515,7 @@ Works only if all objects have the same values.</string>
</property>
</widget>
</item>
<item row="4" column="1">
<item row="5" column="1">
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@@ -384,7 +528,7 @@ Works only if all objects have the same values.</string>
</property>
</spacer>
</item>
<item row="4" column="2">
<item row="5" column="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@@ -394,7 +538,7 @@ Works only if all objects have the same values.</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="3">
<item row="6" column="0" colspan="3">
<widget class="QCheckBox" name="keepOnlyCutCB">
<property name="toolTip">
<string>When the dialog is closed,
@@ -405,7 +549,7 @@ only created cuts will be visible</string>
</property>
</widget>
</item>
<item row="6" column="2">
<item row="7" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@@ -472,9 +616,9 @@ only created cuts will be visible</string>
</hints>
</connection>
<connection>
<sender>AutoCutfaceColor</sender>
<sender>autoCutfaceColorCB</sender>
<signal>toggled(bool)</signal>
<receiver>CutTransparency</receiver>
<receiver>CutTransparencyHS</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
@@ -488,7 +632,7 @@ only created cuts will be visible</string>
</hints>
</connection>
<connection>
<sender>AutoCutfaceColor</sender>
<sender>autoCutfaceColorCB</sender>
<signal>toggled(bool)</signal>
<receiver>CutColor</receiver>
<slot>setDisabled(bool)</slot>
@@ -503,5 +647,37 @@ only created cuts will be visible</string>
</hint>
</hints>
</connection>
<connection>
<sender>autoBFColorCB</sender>
<signal>toggled(bool)</signal>
<receiver>BFragColor</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>182</x>
<y>427</y>
</hint>
<hint type="destinationlabel">
<x>119</x>
<y>427</y>
</hint>
</hints>
</connection>
<connection>
<sender>autoBFColorCB</sender>
<signal>toggled(bool)</signal>
<receiver>BFragTransparencyHS</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>182</x>
<y>427</y>
</hint>
<hint type="destinationlabel">
<x>150</x>
<y>454</y>
</hint>
</hints>
</connection>
</connections>
</ui>