diff --git a/src/Mod/Part/Gui/SectionCutting.cpp b/src/Mod/Part/Gui/SectionCutting.cpp index 15c3a69c38..c8312ed1f3 100644 --- a/src/Mod/Part/Gui/SectionCutting.cpp +++ b/src/Mod/Part/Gui/SectionCutting.cpp @@ -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()); - 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(compoundObject); - if (!pcCompound) { - Base::Console().Error( - "SectionCut error: compound is incorrectly named, cannot proceed\n"); - return; + // to later store the childs + std::vector 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(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(property); + BoundCompound = objectShape->getBoundingBox(); + // for BooleanFragments we also need to set the checkbox, transparency and color + ui->groupBoxIntersecting->setChecked(true); + auto pcCompoundBFGO = dynamic_cast(pcCompoundBF); + if (pcCompoundBFGO) { + App::Color compoundColor = pcCompoundBFGO->ShapeColor.getValue(); + ui->BFragColor->setColor(compoundColor.asValue()); + 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 compoundObjects; - pcCompound->Links.getLinks(compoundObjects); - for (auto aCompoundObj : compoundObjects) { + for (auto aCompoundObj : compoundChilds) { App::Link* pcLink = dynamic_cast(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::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(compoundObject); - if (!pcCompoundDel) { - Base::Console().Error( - "SectionCut error: compound is incorrectly named, cannot proceed\n"); - return; + // to later store the childs + std::vector compoundChilds; + + // check if this is a BooleanFragments or a Part::Compound + Part::Compound* pcCompoundDelPart = dynamic_cast(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 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 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(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::iterator it2; - std::vector::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(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 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(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((*itParents)); + for (auto parent : parents) { + if (parent->getTypeId() == Base::Type::fromName("App::Part")) { + App::Part* pcPartParent = static_cast(parent); auto placement = Base::freecad_dynamic_cast( 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::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::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(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::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(CutCompoundPartObj); + // add the link to the compound + CutCompoundPart->Links.setValue(ObjectsListLinks); + // set the transparency + auto vpCompound = dynamic_cast( + 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()); 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()); + 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(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(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(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(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(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(CompoundBFVP); + if (CutCompoundBFGeom) { + App::Color BFColor; + BFColor.setValue(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(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" diff --git a/src/Mod/Part/Gui/SectionCutting.h b/src/Mod/Part/Gui/SectionCutting.h index 62c5307860..6a33d8dad1 100644 --- a/src/Mod/Part/Gui/SectionCutting.h +++ b/src/Mod/Part/Gui/SectionCutting.h @@ -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; std::vector 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 diff --git a/src/Mod/Part/Gui/SectionCutting.ui b/src/Mod/Part/Gui/SectionCutting.ui index 47067d8e0b..64f522bee0 100644 --- a/src/Mod/Part/Gui/SectionCutting.ui +++ b/src/Mod/Part/Gui/SectionCutting.ui @@ -7,7 +7,7 @@ 0 0 230 - 427 + 514 @@ -19,7 +19,7 @@ Permanent Section Cutting - + @@ -275,7 +275,7 @@ 16777215 - + 203 203 @@ -291,7 +291,7 @@ - + 0 @@ -301,7 +301,8 @@ If checked, the color and transparency will be taken from the cut objects. -Works only if all objects have the same values. +Works only properly if all objects +have the same values. Auto @@ -334,7 +335,7 @@ Works only if all objects have the same values. - + false @@ -358,7 +359,150 @@ Works only if all objects have the same values. - + + + + + 215 + 16777215 + + + + Allows to cut objects intersecting each other +for the price that all cut objects +will get the same color + + + Cut intersecting objects + + + true + + + false + + + + + + + 0 + 0 + + + + Color of cut face + + + Color + + + + + + + false + + + + 50 + 20 + + + + + 16777215 + 16777215 + + + + Color for all objects + + + + 203 + 203 + 203 + + + + DefaultShapeColor + + + View + + + + + + + + 0 + 0 + + + + If checked, the color and transparency +will be taken from the cut objects. +Works only properly if all objects +have the same values. + + + Auto + + + true + + + TwoSideRendering + + + Mod/Part + + + + + + + + 0 + 0 + + + + Transparency of cut face + + + Transparency + + + + + + + false + + + 0 % + + + 100 + + + Qt::Horizontal + + + DefaultShapeTransparency + + + View + + + + + + + true @@ -371,7 +515,7 @@ Works only if all objects have the same values. - + Qt::Horizontal @@ -384,7 +528,7 @@ Works only if all objects have the same values. - + Qt::Horizontal @@ -394,7 +538,7 @@ Works only if all objects have the same values. - + When the dialog is closed, @@ -405,7 +549,7 @@ only created cuts will be visible - + Qt::Vertical @@ -472,9 +616,9 @@ only created cuts will be visible - AutoCutfaceColor + autoCutfaceColorCB toggled(bool) - CutTransparency + CutTransparencyHS setDisabled(bool) @@ -488,7 +632,7 @@ only created cuts will be visible - AutoCutfaceColor + autoCutfaceColorCB toggled(bool) CutColor setDisabled(bool) @@ -503,5 +647,37 @@ only created cuts will be visible + + autoBFColorCB + toggled(bool) + BFragColor + setDisabled(bool) + + + 182 + 427 + + + 119 + 427 + + + + + autoBFColorCB + toggled(bool) + BFragTransparencyHS + setDisabled(bool) + + + 182 + 427 + + + 150 + 454 + + +