1651 lines
69 KiB
C++
1651 lines
69 KiB
C++
/***************************************************************************
|
|
* Copyright (c) 2022 Uwe Stöhr <uwestoehr@lyx.org> *
|
|
* *
|
|
* This file is part of the FreeCAD CAx development system. *
|
|
* *
|
|
* This library is free software; you can redistribute it and/or *
|
|
* modify it under the terms of the GNU Library General Public *
|
|
* License as published by the Free Software Foundation; either *
|
|
* version 2 of the License, or (at your option) any later version. *
|
|
* *
|
|
* This library 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 Library General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU Library General Public *
|
|
* License along with this library; see the file COPYING.LIB. If not, *
|
|
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
|
* Suite 330, Boston, MA 02111-1307, USA *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include "PreCompiled.h"
|
|
|
|
#ifndef _PreComp_
|
|
|
|
// to avoid compiler warnings of redefining contents of basic.h
|
|
// later by #include <Gui/ViewProviderGeometryObject.h>
|
|
# define _USE_MATH_DEFINES
|
|
# include <cmath>
|
|
|
|
# include <Inventor/actions/SoGetBoundingBoxAction.h>
|
|
# include <Inventor/nodes/SoCamera.h>
|
|
# include <Inventor/nodes/SoOrthographicCamera.h>
|
|
# include <QDialog>
|
|
# include <QDockWidget>
|
|
# include <QDoubleSpinBox>
|
|
# include <QSlider>
|
|
# include <QToolTip>
|
|
#endif
|
|
|
|
#include <App/Document.h>
|
|
#include <App/Link.h>
|
|
#include <App/Part.h>
|
|
#include <Base/UnitsApi.h>
|
|
#include <Gui/Application.h>
|
|
#include <Gui/Command.h>
|
|
#include <Gui/DockWindowManager.h>
|
|
#include <Gui/Document.h>
|
|
#include <Gui/MainWindow.h>
|
|
#include <Gui/PrefWidgets.h>
|
|
#include <Gui/View3DInventor.h>
|
|
#include <Gui/View3DInventorViewer.h>
|
|
#include <Gui/ViewProviderGeometryObject.h>
|
|
#include <Mod/Part/App/FeatureCompound.h>
|
|
#include <Mod/Part/App/FeaturePartBox.h>
|
|
#include <Mod/Part/App/FeaturePartCommon.h>
|
|
#include <Mod/Part/App/FeaturePartCut.h>
|
|
#include <Mod/Part/App/FeaturePartFuse.h>
|
|
#include <Mod/Part/App/PartFeatures.h>
|
|
|
|
#include "SectionCutting.h"
|
|
#include "ui_SectionCutting.h"
|
|
|
|
|
|
using namespace PartGui;
|
|
|
|
enum Refresh : bool
|
|
{
|
|
notXValue = false,
|
|
notYValue = false,
|
|
notZValue = false,
|
|
notXRange = false,
|
|
notYRange = false,
|
|
notZRange = false,
|
|
XValue = true,
|
|
YValue = true,
|
|
ZValue = true,
|
|
XRange = true,
|
|
YRange = true,
|
|
ZRange = true
|
|
};
|
|
|
|
SectionCut::SectionCut(QWidget* parent)
|
|
: QDialog(parent)
|
|
, ui(new Ui_SectionCut)
|
|
{
|
|
// create widgets
|
|
ui->setupUi(this);
|
|
ui->cutX->setRange(-INT_MAX, INT_MAX);
|
|
ui->cutY->setRange(-INT_MAX, INT_MAX);
|
|
ui->cutZ->setRange(-INT_MAX, INT_MAX);
|
|
|
|
// get all objects in the document
|
|
auto docGui = Gui::Application::Instance->activeDocument();
|
|
if (!docGui) {
|
|
Base::Console().Error("SectionCut error: there is no document\n");
|
|
return;
|
|
}
|
|
doc = docGui->getDocument();
|
|
if (!doc) {
|
|
Base::Console().Error("SectionCut error: there is no document\n");
|
|
return;
|
|
}
|
|
|
|
std::vector<App::DocumentObject*> ObjectsList = doc->getObjects();
|
|
if (ObjectsList.empty()) {
|
|
Base::Console().Error("SectionCut error: there are no objects in the document\n");
|
|
return;
|
|
}
|
|
// now store those that are currently visible
|
|
for (auto it = ObjectsList.begin(); it != ObjectsList.end(); ++it) {
|
|
if ((*it)->Visibility.getValue())
|
|
ObjectsListVisible.push_back(*it);
|
|
}
|
|
|
|
// lambda function to set color and transparency
|
|
auto setColorTransparency = [&](Part::Box* pcBox) {
|
|
App::Color cutColor;
|
|
long cutTransparency;
|
|
auto vpBox = dynamic_cast<Gui::ViewProviderGeometryObject*>(
|
|
Gui::Application::Instance->getViewProvider(pcBox));
|
|
if (vpBox) {
|
|
cutColor = vpBox->ShapeColor.getValue();
|
|
cutTransparency = vpBox->Transparency.getValue();
|
|
ui->CutColor->setColor(cutColor.asValue<QColor>());
|
|
ui->CutTransparency->setValue(cutTransparency);
|
|
ui->CutTransparency->setToolTip(QString::number(cutTransparency) + QString::fromLatin1(" %"));
|
|
}
|
|
};
|
|
|
|
// if we can have existing cut boxes, take their values
|
|
// to access the flip state we must compare the bounding boxes of the cutbox and the compound
|
|
Base::BoundBox3d BoundCompound;
|
|
Base::BoundBox3d BoundCutBox;
|
|
if (doc->getObject(BoxXName) || doc->getObject(BoxYName) || doc->getObject(BoxZName)) {
|
|
// automatic coloring must be disabled
|
|
ui->AutoCutfaceColor->setChecked(false);
|
|
if (doc->getObject(CompoundName)) {
|
|
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;
|
|
}
|
|
BoundCompound = pcCompound->Shape.getBoundingBox();
|
|
}
|
|
}
|
|
if (doc->getObject(BoxZName)) {
|
|
Part::Box* pcBox = dynamic_cast<Part::Box*>(doc->getObject(BoxZName));
|
|
if (!pcBox) {
|
|
Base::Console().Error("SectionCut error: cut box is incorrectly named, cannot proceed\n");
|
|
return;
|
|
}
|
|
hasBoxZ = true;
|
|
ui->groupBoxZ->setChecked(true);
|
|
// if z of cutbox bounding is greater than z of compound bounding
|
|
// we know that the cutbox is in flipped state
|
|
BoundCutBox = pcBox->Shape.getBoundingBox();
|
|
if (BoundCutBox.MinZ > BoundCompound.MinZ){
|
|
ui->cutZ->setValue(pcBox->Placement.getValue().getPosition().z);
|
|
ui->flipZ->setChecked(true);
|
|
}
|
|
else {
|
|
ui->cutZ->setValue(pcBox->Height.getValue() + pcBox->Placement.getValue().getPosition().z);
|
|
ui->flipZ->setChecked(false);
|
|
}
|
|
// set color and transparency
|
|
setColorTransparency(pcBox);
|
|
}
|
|
if (doc->getObject(BoxYName)) {
|
|
Part::Box* pcBox = dynamic_cast<Part::Box*>(doc->getObject(BoxYName));
|
|
if (!pcBox) {
|
|
Base::Console().Error("SectionCut error: cut box is incorrectly named, cannot proceed\n");
|
|
return;
|
|
}
|
|
hasBoxY = true;
|
|
ui->groupBoxY->setChecked(true);
|
|
BoundCutBox = pcBox->Shape.getBoundingBox();
|
|
if (BoundCutBox.MinY > BoundCompound.MinY) {
|
|
ui->cutY->setValue(pcBox->Placement.getValue().getPosition().y);
|
|
ui->flipY->setChecked(true);
|
|
}
|
|
else {
|
|
ui->cutY->setValue(pcBox->Width.getValue() + pcBox->Placement.getValue().getPosition().y);
|
|
ui->flipY->setChecked(false);
|
|
}
|
|
setColorTransparency(pcBox);
|
|
}
|
|
if (doc->getObject(BoxXName)) {
|
|
Part::Box* pcBox = dynamic_cast<Part::Box*>(doc->getObject(BoxXName));
|
|
if (!pcBox) {
|
|
Base::Console().Error("SectionCut error: cut box is incorrectly named, cannot proceed\n");
|
|
return;
|
|
}
|
|
hasBoxX = true;
|
|
ui->groupBoxX->setChecked(true);
|
|
BoundCutBox = pcBox->Shape.getBoundingBox();
|
|
if (BoundCutBox.MinX > BoundCompound.MinX) {
|
|
ui->cutX->setValue(pcBox->Placement.getValue().getPosition().x);
|
|
ui->flipX->setChecked(true);
|
|
}
|
|
else {
|
|
ui->cutX->setValue(pcBox->Length.getValue() + pcBox->Placement.getValue().getPosition().x);
|
|
ui->flipX->setChecked(false);
|
|
}
|
|
setColorTransparency(pcBox);
|
|
}
|
|
|
|
// hide existing cuts to check if there are objects to be cut visible
|
|
if (doc->getObject(CutXName))
|
|
doc->getObject(CutXName)->Visibility.setValue(false);
|
|
if (doc->getObject(CutYName))
|
|
doc->getObject(CutYName)->Visibility.setValue(false);
|
|
if (doc->getObject(CutZName))
|
|
doc->getObject(CutZName)->Visibility.setValue(false);
|
|
|
|
// get bounding box
|
|
SbBox3f box = getViewBoundingBox();
|
|
if (!box.isEmpty()) {
|
|
// if there is a cut box, perform the cut
|
|
if (hasBoxX || hasBoxY || hasBoxZ) {
|
|
// refresh only the range since we set the values above already
|
|
refreshCutRanges(box, Refresh::notXValue, Refresh::notYValue, Refresh::notZValue,
|
|
Refresh::XRange, Refresh::YRange, Refresh::ZRange);
|
|
}
|
|
else
|
|
refreshCutRanges(box);
|
|
}
|
|
// the case of an empty box and having cuts will be handles later by startCutting(true)
|
|
|
|
connect(ui->groupBoxX, &QGroupBox::toggled, this, &SectionCut::onGroupBoxXtoggled);
|
|
connect(ui->groupBoxY, &QGroupBox::toggled, this, &SectionCut::onGroupBoxYtoggled);
|
|
connect(ui->groupBoxZ, &QGroupBox::toggled, this, &SectionCut::onGroupBoxZtoggled);
|
|
connect(ui->cutX, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &SectionCut::onCutXvalueChanged);
|
|
connect(ui->cutY, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &SectionCut::onCutYvalueChanged);
|
|
connect(ui->cutZ, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &SectionCut::onCutZvalueChanged);
|
|
connect(ui->cutXHS, &QSlider::sliderMoved, this, &SectionCut::onCutXHSsliderMoved);
|
|
connect(ui->cutYHS, &QSlider::sliderMoved, this, &SectionCut::onCutYHSsliderMoved);
|
|
connect(ui->cutZHS, &QSlider::sliderMoved, this, &SectionCut::onCutZHSsliderMoved);
|
|
connect(ui->cutXHS, &QSlider::valueChanged, this, &SectionCut::onCutXHSChanged);
|
|
connect(ui->cutYHS, &QSlider::valueChanged, this, &SectionCut::onCutYHSChanged);
|
|
connect(ui->cutZHS, &QSlider::valueChanged, this, &SectionCut::onCutZHSChanged);
|
|
connect(ui->flipX, &QPushButton::clicked, this, &SectionCut::onFlipXclicked);
|
|
connect(ui->flipY, &QPushButton::clicked, this, &SectionCut::onFlipYclicked);
|
|
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);
|
|
|
|
// if there is a cut, perform it
|
|
if (hasBoxX || hasBoxY || hasBoxZ) {
|
|
ui->RefreshCutPB->setEnabled(false);
|
|
startCutting(true);
|
|
}
|
|
}
|
|
|
|
// actions to be done when document was closed
|
|
void SectionCut::noDocumentActions()
|
|
{
|
|
ui->groupBoxX->blockSignals(true);
|
|
ui->groupBoxY->blockSignals(true);
|
|
ui->groupBoxZ->blockSignals(true);
|
|
doc = nullptr;
|
|
// reset the cut group boxes
|
|
ui->groupBoxX->setChecked(false);
|
|
ui->groupBoxY->setChecked(false);
|
|
ui->groupBoxZ->setChecked(false);
|
|
ui->RefreshCutPB->setEnabled(true);
|
|
ui->groupBoxX->blockSignals(false);
|
|
ui->groupBoxY->blockSignals(false);
|
|
ui->groupBoxZ->blockSignals(false);
|
|
}
|
|
|
|
void SectionCut::startCutting(bool isInitial)
|
|
{
|
|
// there might be no document
|
|
if (!Gui::Application::Instance->activeDocument()) {
|
|
noDocumentActions();
|
|
return;
|
|
}
|
|
// the document might have been changed
|
|
if (doc != Gui::Application::Instance->activeDocument()->getDocument())
|
|
// refresh documents list
|
|
onRefreshCutPBclicked();
|
|
|
|
App::DocumentObject* anObject = nullptr;
|
|
std::vector<App::DocumentObjectT>::iterator it;
|
|
|
|
// lambda function to delete objects
|
|
auto deleteObject = [&](const char* objectName) {
|
|
anObject = doc->getObject(objectName);
|
|
// the deleted object might have been visible before, thus check and delete it from the list
|
|
auto found = std::find_if(ObjectsListVisible.begin(), ObjectsListVisible.end(), [anObject](const App::DocumentObjectT& obj) {
|
|
return (obj.getObject() == anObject);
|
|
});
|
|
if (found != ObjectsListVisible.end())
|
|
ObjectsListVisible.erase(found);
|
|
doc->removeObject(objectName);
|
|
};
|
|
|
|
// 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
|
|
// to uncheck cutting planes and to add/remove objects while this dialog is open
|
|
// We must remove in this order because the tree hierary of the features is Z->Y->X and Cut->Box
|
|
|
|
if (doc->getObject(CutZName))
|
|
deleteObject(CutZName);
|
|
if (doc->getObject(BoxZName))
|
|
deleteObject(BoxZName);
|
|
if (doc->getObject(CutYName))
|
|
deleteObject(CutYName);
|
|
if (doc->getObject(BoxYName))
|
|
deleteObject(BoxYName);
|
|
if (doc->getObject(CutXName))
|
|
deleteObject(CutXName);
|
|
if (doc->getObject(BoxXName))
|
|
deleteObject(BoxXName);
|
|
if (doc->getObject(CompoundName)) {
|
|
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;
|
|
}
|
|
std::vector<App::DocumentObject*> compoundObjects;
|
|
pcCompoundDel->Links.getLinks(compoundObjects);
|
|
// first delete the compound
|
|
auto foundObj = std::find_if(ObjectsListVisible.begin(), ObjectsListVisible.end(), [anObject](const App::DocumentObjectT& obj) {
|
|
return (obj.getObject() == anObject);
|
|
});
|
|
if (foundObj != ObjectsListVisible.end())
|
|
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());
|
|
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());
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
else {
|
|
// we must refresh the ObjectsListVisible list
|
|
onRefreshCutPBclicked();
|
|
}
|
|
}
|
|
|
|
// we enable the sliders because for assemblies we disabled them
|
|
ui->cutXHS->setEnabled(true);
|
|
ui->cutYHS->setEnabled(true);
|
|
ui->cutZHS->setEnabled(true);
|
|
|
|
// ObjectsListVisible contains all visible objects of the document, but we can only cut
|
|
// those that have a solid shape
|
|
std::vector<App::DocumentObject*> ObjectsListCut;
|
|
bool isLinkAssembly = false;
|
|
for (it = ObjectsListVisible.begin(); it != ObjectsListVisible.end(); ++it) {
|
|
// we need all Link objects in App::Part for example for Assembly 4
|
|
if (it->getObject()->getTypeId() == Base::Type::fromName("App::Part")) {
|
|
App::Part* pcPart = static_cast<App::Part*>(it->getObject());
|
|
|
|
// 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));
|
|
// we assume that App::Links inside a App::Part are an assembly
|
|
isLinkAssembly = true;
|
|
}
|
|
}
|
|
}
|
|
// get all shapes that are also Part::Features
|
|
if (it->getObject()->getPropertyByName("Shape")
|
|
&& it->getObject()->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 (!it->getObject()->getTypeId().isDerivedFrom(Base::Type::fromName("Part::Part2DObject"))
|
|
&& !it->getObject()->getTypeId().isDerivedFrom(Base::Type::fromName("Part::Datum"))
|
|
&& !it->getObject()->getTypeId().isDerivedFrom(Base::Type::fromName("PartDesign::Feature"))
|
|
&& !it->getObject()->getTypeId().isDerivedFrom(Base::Type::fromName("Part::Compound"))
|
|
&& it->getObject()->getTypeId() != Base::Type::fromName("App::Part"))
|
|
ObjectsListCut.push_back(it->getObject());
|
|
}
|
|
// get Links that are derived from Part objects
|
|
if (it->getObject()->getTypeId() == Base::Type::fromName("App::Link")) {
|
|
App::Link* pcLink = static_cast<App::Link*>(it->getObject());
|
|
auto linkedObject = doc->getObject(pcLink->LinkedObject.getObjectName());
|
|
if (linkedObject && linkedObject->getTypeId().isDerivedFrom(Base::Type::fromName("Part::Feature")))
|
|
ObjectsListCut.push_back(it->getObject());
|
|
}
|
|
}
|
|
|
|
if (isLinkAssembly) {
|
|
// we disable the sliders because for assemblies it will takes ages to do several dozen recomputes
|
|
QString SliderToolTip = tr("Sliders are disabled for assemblies");
|
|
ui->cutXHS->setEnabled(false);
|
|
ui->cutXHS->setToolTip(SliderToolTip);
|
|
ui->cutYHS->setEnabled(false);
|
|
ui->cutYHS->setToolTip(SliderToolTip);
|
|
ui->cutZHS->setEnabled(false);
|
|
ui->cutZHS->setToolTip(SliderToolTip);
|
|
}
|
|
|
|
// 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) {
|
|
if ( it->getObject()->getTypeId().isDerivedFrom(Base::Type::fromName("Part::Boolean"))
|
|
|| it->getObject()->getTypeId().isDerivedFrom(Base::Type::fromName("Part::MultiCommon"))
|
|
|| it->getObject()->getTypeId().isDerivedFrom(Base::Type::fromName("Part::MultiFuse"))
|
|
|| it->getObject()->getTypeId().isDerivedFrom(Base::Type::fromName("Part::Thickness"))
|
|
|| it->getObject()->getTypeId().isDerivedFrom(Base::Type::fromName("Part::FilletBase")) ) {
|
|
// get possible links
|
|
auto subObjectList = it->getObject()->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);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// we might have no objects that can be cut
|
|
if (ObjectsListCut.empty()) {
|
|
if (isInitial)
|
|
Base::Console().Error("SectionCut error: there are no visible objects to be cut\n");
|
|
else
|
|
Base::Console().Error("SectionCut error: there are no objects in the document that can be cut\n");
|
|
// block signals to be able to reset the cut group boxes without calling startCutting again
|
|
ui->groupBoxX->blockSignals(true);
|
|
ui->groupBoxY->blockSignals(true);
|
|
ui->groupBoxZ->blockSignals(true);
|
|
ui->groupBoxX->setChecked(false);
|
|
ui->groupBoxY->setChecked(false);
|
|
ui->groupBoxZ->setChecked(false);
|
|
ui->RefreshCutPB->setEnabled(true);
|
|
ui->groupBoxX->blockSignals(false);
|
|
ui->groupBoxY->blockSignals(false);
|
|
ui->groupBoxZ->blockSignals(false);
|
|
return;
|
|
}
|
|
|
|
// we cut this way:
|
|
// 1. put all existing objects into a part compound
|
|
// 2. create a box with the size of the bounding box
|
|
// 3. cut the box from the compound
|
|
|
|
// depending on how many cuts should be performed, we need as many boxes
|
|
// if nothing is yet to be cut, we can return
|
|
if (!ui->groupBoxX->isChecked() && !ui->groupBoxY->isChecked()
|
|
&& !ui->groupBoxZ->isChecked()) {
|
|
// there is no active cut, so we can enable refresh button
|
|
ui->RefreshCutPB->setEnabled(true);
|
|
return;
|
|
}
|
|
|
|
// 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;
|
|
bool autoColor = true;
|
|
bool autoTransparency = true;
|
|
auto vpFirstObject = dynamic_cast<Gui::ViewProviderGeometryObject*>(
|
|
Gui::Application::Instance->getViewProvider(*ObjectsListCut.begin()));
|
|
if (vpFirstObject) {
|
|
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++) {
|
|
// 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();
|
|
else
|
|
newName = (*itCuts)->getNameInDocument();
|
|
newName = newName + "_CutLink";
|
|
|
|
auto newObject = doc->addObject("App::Link", newName.c_str());
|
|
if (!newObject) {
|
|
Base::Console().Error("SectionCut error: 'App::Link' could not be added\n");
|
|
return;
|
|
}
|
|
App::Link* pcLink = static_cast<App::Link*>(newObject);
|
|
// set the object to the created empty link object
|
|
pcLink->LinkedObject.setValue((*itCuts));
|
|
// we want to get the link at the same position as the original
|
|
pcLink->LinkTransform.setValue(true);
|
|
|
|
// if the object is part of an App::Part container, the link needs to get the container placement
|
|
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));
|
|
auto placement = Base::freecad_dynamic_cast<App::PropertyPlacement>(
|
|
pcPartParent->getPropertyByName("Placement"));
|
|
if (placement)
|
|
pcLink->Placement.setValue(placement->getValue());
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
|
|
// check if all objects have same color and transparency
|
|
if (ui->AutoCutfaceColor->isChecked()) {
|
|
auto vpObject = dynamic_cast<Gui::ViewProviderGeometryObject*>(
|
|
Gui::Application::Instance->getViewProvider(*itCuts));
|
|
if (vpObject) {
|
|
if (cutColor != vpObject->ShapeColor.getValue())
|
|
autoColor = false;
|
|
if (cutTransparency != vpObject->Transparency.getValue())
|
|
autoTransparency = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// compute the filled compound
|
|
pcCompound->recomputeFeature();
|
|
|
|
// make all objects invisible so that only the compound remains
|
|
for (it = ObjectsListVisible.begin(); it != ObjectsListVisible.end(); ++it) {
|
|
it->getObject()->Visibility.setValue(false);
|
|
}
|
|
|
|
// the area in which we can cut is the size of the compound
|
|
// we get its size by its bounding box
|
|
SbBox3f CompoundBoundingBox = getViewBoundingBox();
|
|
if (CompoundBoundingBox.isEmpty()) {
|
|
Base::Console().Error("SectionCut error: the CompoundBoundingBox is empty\n");
|
|
return;
|
|
}
|
|
|
|
// store the current cut positions te reset them later if possible
|
|
double CutPosX = ui->cutX->value();
|
|
double CutPosY = ui->cutY->value();
|
|
double CutPosZ = ui->cutZ->value();
|
|
|
|
// refresh all cut limits according to the new bounding box
|
|
refreshCutRanges(CompoundBoundingBox);
|
|
|
|
// prepare the cut box size according to the bounding box size
|
|
std::vector<float> BoundingBoxSize = { 0.0, 0.0, 0.0 };
|
|
CompoundBoundingBox.getSize(BoundingBoxSize[0], BoundingBoxSize[1], BoundingBoxSize[2]);
|
|
// get placement of the bounding box origin
|
|
std::vector<float> BoundingBoxOrigin = { 0.0, 0.0, 0.0 };
|
|
CompoundBoundingBox.getOrigin(BoundingBoxOrigin[0], BoundingBoxOrigin[1], BoundingBoxOrigin[2]);
|
|
|
|
// now we can create the cut boxes
|
|
Base::Vector3d BoxOriginSet;
|
|
Base::Placement placement;
|
|
SbBox3f CutBoundingBox;
|
|
hasBoxX = false;
|
|
hasBoxY = false;
|
|
hasBoxZ = false;
|
|
hasBoxCustom = false;
|
|
|
|
// if automatic, we take this color for the cut
|
|
if (ui->AutoCutfaceColor->isChecked()) {
|
|
if (autoColor) {
|
|
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);
|
|
}
|
|
}
|
|
|
|
// read cutface color for the cut box
|
|
App::Color boxColor;
|
|
boxColor.setValue<QColor>(ui->CutColor->color());
|
|
int boxTransparency = ui->CutTransparency->value();
|
|
|
|
if (ui->groupBoxX->isChecked()) {
|
|
// create a box
|
|
auto CutBox = doc->addObject("Part::Box", BoxXName);
|
|
if (!CutBox) {
|
|
Base::Console().Error( (std::string("SectionCut error: ")
|
|
+ std::string(BoxXName) + std::string(" could not be added\n")).c_str() );
|
|
return;
|
|
}
|
|
Part::Box* pcBox = static_cast<Part::Box*>(CutBox);
|
|
// it appears that because of internal rounding errors, the bounding box is sometimes
|
|
// a bit too small, for example for epplipsoides, thus make the box a bit larger
|
|
pcBox->Length.setValue(BoundingBoxSize[0] + 1.0);
|
|
pcBox->Width.setValue(BoundingBoxSize[1] + 1.0);
|
|
pcBox->Height.setValue(BoundingBoxSize[2] + 1.0);
|
|
// set the previous cut value because refreshCutRanges changed it
|
|
// in case the there was previously no cut, nothing will actually be changed
|
|
// the previous value might now be outside the current possible range, then reset it
|
|
if (CutPosX >= ui->cutX->maximum()) {
|
|
CutPosX = ui->cutX->maximum() - 0.1; // short below the maximum
|
|
}
|
|
else if (CutPosX <= ui->cutX->minimum()) {
|
|
CutPosX = ui->cutX->minimum() + 0.1; // short above the minimum
|
|
}
|
|
// 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
|
|
if (!ui->flipX->isChecked())
|
|
BoxOriginSet.x = CutPosX - (BoundingBoxSize[0] + 1.0);
|
|
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
|
|
pcBox->Placement.setValue(placement);
|
|
auto vpBox = dynamic_cast<Gui::ViewProviderGeometryObject*>(
|
|
Gui::Application::Instance->getViewProvider(pcBox));
|
|
if (vpBox) {
|
|
vpBox->ShapeColor.setValue(boxColor);
|
|
vpBox->Transparency.setValue(boxTransparency);
|
|
}
|
|
|
|
// create a cut feature
|
|
auto CutFeature = doc->addObject("Part::Cut", CutXName);
|
|
if (!CutFeature) {
|
|
Base::Console().Error( (std::string("SectionCut error: ")
|
|
+ std::string(CutXName) + std::string(" could not be added\n")).c_str() );
|
|
return;
|
|
}
|
|
Part::Cut* pcCut = static_cast<Part::Cut*>(CutFeature);
|
|
pcCut->Base.setValue(CutCompound);
|
|
pcCut->Tool.setValue(CutBox);
|
|
|
|
// set the cut value
|
|
ui->cutX->setValue(CutPosX);
|
|
// recomputing recursively is especially for assemblies very time-consuming
|
|
// however there must be a final recursicve recompute and we do this at the end
|
|
// so only recomute recursively if there are no other cuts
|
|
if (!ui->groupBoxY->isChecked() && !ui->groupBoxZ->isChecked())
|
|
pcCut->recomputeFeature(true);
|
|
else
|
|
pcCut->recomputeFeature(false);
|
|
hasBoxX = true;
|
|
}
|
|
if (ui->groupBoxY->isChecked()) {
|
|
// if there is a X cut, its size defines the possible range for the Y cut
|
|
// the cut box size is not affected, it can be as large as the compound
|
|
if (hasBoxX) {
|
|
CutBoundingBox = getViewBoundingBox();
|
|
// refresh the Y cut limits according to the new bounding box
|
|
refreshCutRanges(CutBoundingBox, Refresh::notXValue, Refresh::notYValue, Refresh::notZValue,
|
|
Refresh::notXRange, Refresh::YRange, Refresh::notZRange);
|
|
}
|
|
auto CutBox = doc->addObject("Part::Box", BoxYName);
|
|
if (!CutBox) {
|
|
Base::Console().Error((std::string("SectionCut error: ")
|
|
+ std::string(BoxYName) + std::string(" could not be added\n")).c_str() );
|
|
return;
|
|
}
|
|
Part::Box* pcBox = static_cast<Part::Box*>(CutBox);
|
|
pcBox->Length.setValue(BoundingBoxSize[0] + 1.0);
|
|
pcBox->Width.setValue(BoundingBoxSize[1] + 1.0);
|
|
pcBox->Height.setValue(BoundingBoxSize[2] + 1.0);
|
|
// reset previous cut value
|
|
if (CutPosY >= ui->cutY->maximum()) {
|
|
CutPosY = ui->cutY->maximum() - 0.1; // short below the maximum
|
|
}
|
|
else if (CutPosY <= ui->cutY->minimum()) {
|
|
CutPosY = ui->cutY->minimum() + 0.1; // short above the minimum
|
|
}
|
|
//set the box position
|
|
BoxOriginSet.x = BoundingBoxOrigin[0] - 0.5;
|
|
if (!ui->flipY->isChecked())
|
|
BoxOriginSet.y = CutPosY - (BoundingBoxSize[1] + 1.0);
|
|
else //flipped
|
|
BoxOriginSet.y = CutPosY;
|
|
BoxOriginSet.z = BoundingBoxOrigin[2] - 0.5;
|
|
placement.setPosition(BoxOriginSet);
|
|
pcBox->Placement.setValue(placement);
|
|
auto vpBox = dynamic_cast<Gui::ViewProviderGeometryObject*>(
|
|
Gui::Application::Instance->getViewProvider(pcBox));
|
|
if (vpBox) {
|
|
vpBox->ShapeColor.setValue(boxColor);
|
|
vpBox->Transparency.setValue(boxTransparency);
|
|
}
|
|
|
|
auto CutFeature = doc->addObject("Part::Cut", CutYName);
|
|
if (!CutFeature) {
|
|
Base::Console().Error((std::string("SectionCut error: ")
|
|
+ std::string(CutYName) + std::string(" could not be added\n")).c_str() );
|
|
return;
|
|
}
|
|
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)
|
|
pcCut->Base.setValue(doc->getObject(CutXName));
|
|
else
|
|
pcCut->Base.setValue(CutCompound);
|
|
pcCut->Tool.setValue(CutBox);
|
|
|
|
// set the cut value
|
|
ui->cutY->setValue(CutPosY);
|
|
if (!ui->groupBoxZ->isChecked())
|
|
pcCut->recomputeFeature(true);
|
|
else
|
|
pcCut->recomputeFeature(false);
|
|
hasBoxY = true;
|
|
}
|
|
if (ui->groupBoxZ->isChecked()) {
|
|
if (hasBoxX || hasBoxY) {
|
|
CutBoundingBox = getViewBoundingBox();
|
|
refreshCutRanges(CutBoundingBox, Refresh::notXValue, Refresh::notYValue, Refresh::notZValue,
|
|
Refresh::notXRange, Refresh::notYRange, Refresh::ZRange);
|
|
}
|
|
auto CutBox = doc->addObject("Part::Box", BoxZName);
|
|
if (!CutBox) {
|
|
Base::Console().Error((std::string("SectionCut error: ")
|
|
+ std::string(BoxZName) + std::string(" could not be added\n")).c_str() );
|
|
return;
|
|
}
|
|
Part::Box* pcBox = static_cast<Part::Box*>(CutBox);
|
|
pcBox->Length.setValue(BoundingBoxSize[0] + 1.0);
|
|
pcBox->Width.setValue(BoundingBoxSize[1] + 1.0);
|
|
pcBox->Height.setValue(BoundingBoxSize[2] + 1.0);
|
|
// reset previous cut value
|
|
if (CutPosZ >= ui->cutZ->maximum()) {
|
|
CutPosZ = ui->cutZ->maximum() - 0.1; // short below the maximum
|
|
}
|
|
else if (CutPosZ <= ui->cutZ->minimum()) {
|
|
CutPosZ = ui->cutZ->minimum() + 0.1; // short above the minimum
|
|
}
|
|
//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
|
|
BoxOriginSet.z = CutPosZ;
|
|
placement.setPosition(BoxOriginSet);
|
|
pcBox->Placement.setValue(placement);
|
|
auto vpBox = dynamic_cast<Gui::ViewProviderGeometryObject*>(
|
|
Gui::Application::Instance->getViewProvider(pcBox));
|
|
if (vpBox) {
|
|
vpBox->ShapeColor.setValue(boxColor);
|
|
vpBox->Transparency.setValue(boxTransparency);
|
|
}
|
|
|
|
auto CutFeature = doc->addObject("Part::Cut", CutZName);
|
|
if (!CutFeature) {
|
|
Base::Console().Error( (std::string("SectionCut error: ")
|
|
+ std::string(CutZName) + std::string(" could not be added\n")).c_str() );
|
|
return;
|
|
}
|
|
Part::Cut* pcCut = static_cast<Part::Cut*>(CutFeature);
|
|
// if there is already a cut, we must take it as feature to be cut
|
|
if (hasBoxY) {
|
|
pcCut->Base.setValue(doc->getObject(CutYName));
|
|
}
|
|
else if (hasBoxX && !hasBoxY) {
|
|
pcCut->Base.setValue(doc->getObject(CutXName));
|
|
}
|
|
else {
|
|
pcCut->Base.setValue(CutCompound);
|
|
}
|
|
pcCut->Tool.setValue(CutBox);
|
|
|
|
// set the cut value
|
|
ui->cutZ->setValue(CutPosZ);
|
|
pcCut->recomputeFeature(true);
|
|
hasBoxZ = true;
|
|
}
|
|
}
|
|
|
|
SectionCut* SectionCut::makeDockWidget(QWidget* parent)
|
|
{
|
|
// embed this dialog into a QDockWidget
|
|
SectionCut* sectionCut = new SectionCut(parent);
|
|
Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance();
|
|
// the dialog is designed that you can see the tree, thus put it to the right side
|
|
QDockWidget* dw = pDockMgr->addDockWindow("Section Cutting", sectionCut, Qt::RightDockWidgetArea);
|
|
dw->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
|
|
//dw->setFloating(true);
|
|
dw->show();
|
|
|
|
return sectionCut;
|
|
}
|
|
|
|
/** Destroys the object and frees any allocated resources */
|
|
SectionCut::~SectionCut()
|
|
{
|
|
// there might be no document
|
|
if (!Gui::Application::Instance->activeDocument()) {
|
|
noDocumentActions();
|
|
return;
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SectionCut::reject()
|
|
{
|
|
QDialog::reject();
|
|
QDockWidget* dw = qobject_cast<QDockWidget*>(parent());
|
|
if (dw) {
|
|
dw->deleteLater();
|
|
}
|
|
}
|
|
|
|
void SectionCut::onGroupBoxXtoggled()
|
|
{
|
|
// reset the cut
|
|
startCutting();
|
|
}
|
|
|
|
void SectionCut::onGroupBoxYtoggled()
|
|
{
|
|
startCutting();
|
|
}
|
|
|
|
void SectionCut::onGroupBoxZtoggled()
|
|
{
|
|
startCutting();
|
|
}
|
|
|
|
// helper function for the onFlip_clicked signal
|
|
void SectionCut::CutValueHelper(double value, QDoubleSpinBox* SpinBox, QSlider* Slider)
|
|
{
|
|
// there might be no document
|
|
if (!Gui::Application::Instance->activeDocument()) {
|
|
noDocumentActions();
|
|
return;
|
|
}
|
|
// refresh objects and return in case the document was changed
|
|
if (doc != Gui::Application::Instance->activeDocument()->getDocument()) {
|
|
onRefreshCutPBclicked();
|
|
return;
|
|
}
|
|
// update slider position and tooltip
|
|
// the slider value is % of the cut range
|
|
if (Slider->isEnabled()) {
|
|
Slider->blockSignals(true);
|
|
Slider->setValue(
|
|
int((value - SpinBox->minimum())
|
|
/ (SpinBox->maximum() - SpinBox->minimum()) * 100.0));
|
|
Slider->setToolTip(QString::number(value, 'g', Base::UnitsApi::getDecimals()));
|
|
Slider->blockSignals(false);
|
|
}
|
|
|
|
// we cannot cut to the edge because then the result is an empty shape
|
|
// we chose purposely not to simply set the range for cutX previously
|
|
// because everything is allowed just not the min/max
|
|
if (SpinBox->value() == SpinBox->maximum()) {
|
|
SpinBox->setValue(SpinBox->maximum() - 0.1);
|
|
return;
|
|
}
|
|
if (SpinBox->value() == SpinBox->minimum()) {
|
|
SpinBox->setValue(SpinBox->minimum() + 0.1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void SectionCut::onCutXvalueChanged(double val)
|
|
{
|
|
CutValueHelper(val, ui->cutX, ui->cutXHS);
|
|
|
|
// get the cut box
|
|
auto CutBox = doc->getObject(BoxXName);
|
|
// when the value has been set after resetting the compound bounding box
|
|
// there is not yet a cut and we do nothing
|
|
if (!CutBox)
|
|
return;
|
|
Part::Box* pcBox = dynamic_cast<Part::Box*>(CutBox);
|
|
if (!pcBox) {
|
|
Base::Console().Error((std::string("SectionCut error: ") + std::string(BoxXName)
|
|
+ std::string(" is no Part::Box object. Cannot proceed.\n")).c_str());
|
|
return;
|
|
}
|
|
// get its placement and size
|
|
Base::Placement placement = pcBox->Placement.getValue();
|
|
Base::Vector3d BoxPosition = placement.getPosition();
|
|
// change the placement
|
|
if (!ui->flipX->isChecked())
|
|
BoxPosition.x = ui->cutX->value() - pcBox->Length.getValue();
|
|
else //flipped
|
|
BoxPosition.x = ui->cutX->value();
|
|
placement.setPosition(BoxPosition);
|
|
pcBox->Placement.setValue(placement);
|
|
|
|
auto CutObject = doc->getObject(CutXName);
|
|
// there should be a box, but maybe the user deleted it meanwhile
|
|
if (!CutObject) {
|
|
Base::Console().Warning((std::string("SectionCut warning: there is no ")
|
|
+ std::string(CutXName) + std::string(", trying to recreate it\n")).c_str());
|
|
// recreate the box
|
|
startCutting();
|
|
return;
|
|
}
|
|
|
|
// if there is another cut, we must recalculate it too
|
|
// we might have cut so that the range for Y and Z is now smaller
|
|
// the hierarchy is always Z->Y->X
|
|
if (hasBoxY && !hasBoxZ) { // only Y
|
|
auto CutFeatureY = doc->getObject(CutYName);
|
|
if (!CutFeatureY) {
|
|
Base::Console().Warning((std::string("SectionCut warning: there is no ")
|
|
+ std::string(CutYName) + std::string(", trying to recreate it\n")).c_str());
|
|
startCutting();
|
|
return;
|
|
}
|
|
// refresh the Y and Z cut limits according to the new bounding box of the cut result
|
|
// make the SectionCutY invisible
|
|
CutFeatureY->Visibility.setValue(false);
|
|
// make SectionCutX visible
|
|
CutObject->Visibility.setValue(true);
|
|
// get new bounding box
|
|
auto CutBoundingBox = getViewBoundingBox();
|
|
// refresh Y limits and Z limits + Z value
|
|
refreshCutRanges(CutBoundingBox, Refresh::notXValue, Refresh::notYValue, Refresh::ZValue,
|
|
Refresh::notXRange, Refresh::YRange, Refresh::ZRange);
|
|
// the value of Y can now be outside or at the limit, in this case reset the value too
|
|
if ((ui->cutY->value() >= ui->cutY->maximum())
|
|
|| (ui->cutY->value() <= ui->cutY->minimum()))
|
|
refreshCutRanges(CutBoundingBox, Refresh::notXValue, Refresh::YValue, Refresh::ZValue,
|
|
Refresh::notXRange, Refresh::YRange, Refresh::ZRange);
|
|
// make the SectionCutY visible again
|
|
CutFeatureY->Visibility.setValue(true);
|
|
// make SectionCutX invisible again
|
|
CutObject->Visibility.setValue(false);
|
|
// recompute the cut
|
|
Part::Cut* pcCutY = static_cast<Part::Cut*>(CutFeatureY);
|
|
pcCutY->recomputeFeature(true);
|
|
}
|
|
else if (hasBoxZ) { // at least Z
|
|
// the main cut is Z, no matter if there is a cut in Y
|
|
auto CutFeatureZ = doc->getObject(CutZName);
|
|
if (!CutFeatureZ) {
|
|
Base::Console().Error((std::string("SectionCut error: there is no ")
|
|
+ std::string(CutZName) + std::string("\n")).c_str());
|
|
return;
|
|
}
|
|
// refresh the Y and Z cut limits according to the new bounding box of the cut result
|
|
// make the SectionCutZ invisible
|
|
CutFeatureZ->Visibility.setValue(false);
|
|
// make SectionCutX visible
|
|
CutObject->Visibility.setValue(true);
|
|
// get new bounding box
|
|
auto CutBoundingBox = getViewBoundingBox();
|
|
// refresh Y and Z limits
|
|
if (hasBoxY) {
|
|
refreshCutRanges(CutBoundingBox, Refresh::notXValue, Refresh::notYValue, Refresh::notZValue,
|
|
Refresh::notXRange, Refresh::YRange, Refresh::ZRange);
|
|
// the value of Y or Z can now be outside or at the limit, in this case reset the value too
|
|
if ((ui->cutY->value() >= ui->cutY->maximum())
|
|
|| (ui->cutY->value() <= ui->cutY->minimum()))
|
|
refreshCutRanges(CutBoundingBox, Refresh::notXValue, Refresh::YValue, Refresh::notZValue,
|
|
Refresh::notXRange, Refresh::YRange, Refresh::ZRange);
|
|
if ((ui->cutZ->value() >= ui->cutZ->maximum())
|
|
|| (ui->cutZ->value() <= ui->cutZ->minimum()))
|
|
refreshCutRanges(CutBoundingBox, Refresh::notXValue, Refresh::notYValue, Refresh::ZValue,
|
|
Refresh::notXRange, Refresh::YRange, Refresh::ZRange);
|
|
}
|
|
else { // there is no Y cut yet so we can set the Y value too
|
|
refreshCutRanges(CutBoundingBox, Refresh::notXValue, Refresh::YValue, Refresh::notZValue,
|
|
Refresh::notXRange, Refresh::YRange, Refresh::ZRange);
|
|
// the value of Z can now be outside or at the limit, in this case reset the value too
|
|
if ((ui->cutZ->value() >= ui->cutZ->maximum())
|
|
|| (ui->cutZ->value() <= ui->cutZ->minimum()))
|
|
refreshCutRanges(CutBoundingBox, Refresh::notXValue, Refresh::YValue, Refresh::ZValue,
|
|
Refresh::notXRange, Refresh::YRange, Refresh::ZRange);
|
|
}
|
|
// make the SectionCutZ visible again
|
|
CutFeatureZ->Visibility.setValue(true);
|
|
// make SectionCutX invisible again
|
|
CutObject->Visibility.setValue(false);
|
|
// recompute the cut
|
|
Part::Cut* pcCutZ = static_cast<Part::Cut*>(CutFeatureZ);
|
|
pcCutZ->recomputeFeature(true);
|
|
}
|
|
else { // just X
|
|
// refresh Y and Z limits + values
|
|
auto CutBoundingBox = getViewBoundingBox();
|
|
refreshCutRanges(CutBoundingBox, Refresh::notXValue, Refresh::YValue, Refresh::ZValue,
|
|
Refresh::notXRange, Refresh::YRange, Refresh::ZRange);
|
|
// recompute the cut
|
|
Part::Cut* pcCut = dynamic_cast<Part::Cut*>(CutObject);
|
|
if (!pcCut) {
|
|
Base::Console().Error((std::string("SectionCut error: ") + std::string(CutZName)
|
|
+ std::string(" is no Part::Cut object. Cannot proceed.\n")).c_str());
|
|
return;
|
|
}
|
|
pcCut->recomputeFeature(true);
|
|
}
|
|
}
|
|
|
|
void SectionCut::onCutXHSsliderMoved(int val)
|
|
{
|
|
// we cannot cut to the edge because then the result is an empty shape
|
|
// we chose purposely not to simply set the range for cutXHS previously
|
|
// because everything is allowed just not the min/max
|
|
// we set it one slider step below the min/max
|
|
if (val == ui->cutXHS->maximum()) {
|
|
ui->cutXHS->setValue(ui->cutXHS->maximum() - ui->cutXHS->singleStep());
|
|
return;
|
|
}
|
|
if (val == ui->cutXHS->minimum()) {
|
|
ui->cutXHS->setValue(ui->cutXHS->minimum() + ui->cutXHS->singleStep());
|
|
return;
|
|
}
|
|
// the slider value is % of the cut range
|
|
double NewCutValue = ui->cutX->minimum()
|
|
+ val / 100.0 * (ui->cutX->maximum() - ui->cutX->minimum());
|
|
ui->cutXHS->setToolTip(QString::number(NewCutValue, 'g', Base::UnitsApi::getDecimals()));
|
|
ui->cutX->setValue(NewCutValue);
|
|
}
|
|
|
|
void SectionCut::onCutXHSChanged(int val)
|
|
{
|
|
onCutXHSsliderMoved(val);
|
|
}
|
|
|
|
void SectionCut::onCutYvalueChanged(double val)
|
|
{
|
|
CutValueHelper(val, ui->cutY, ui->cutYHS);
|
|
|
|
auto CutBox = doc->getObject(BoxYName);
|
|
if (!CutBox)
|
|
return;
|
|
Part::Box* pcBox = dynamic_cast<Part::Box*>(CutBox);
|
|
if (!pcBox) {
|
|
Base::Console().Error((std::string("SectionCut error: ") + std::string(BoxYName)
|
|
+ std::string(" is no Part::Box object. Cannot proceed.\n")).c_str());
|
|
return;
|
|
}
|
|
Base::Placement placement = pcBox->Placement.getValue();
|
|
Base::Vector3d BoxPosition = placement.getPosition();
|
|
if (!ui->flipY->isChecked())
|
|
BoxPosition.y = ui->cutY->value() - pcBox->Width.getValue();
|
|
else //flipped
|
|
BoxPosition.y = ui->cutY->value();
|
|
placement.setPosition(BoxPosition);
|
|
pcBox->Placement.setValue(placement);
|
|
|
|
auto CutObject = doc->getObject(CutYName);
|
|
if (!CutObject) {
|
|
Base::Console().Warning((std::string("SectionCut warning: there is no ")
|
|
+ std::string(CutYName) + std::string(", trying to recreate it\n")).c_str());
|
|
startCutting();
|
|
return;
|
|
}
|
|
|
|
// if there is another cut, we must recalculate it too
|
|
// we might have cut so that the range for Z is now smaller
|
|
// we only need to check for Z since the hierarchy is always Z->Y->X
|
|
if (hasBoxZ) {
|
|
auto CutFeatureZ = doc->getObject(CutZName);
|
|
if (!CutFeatureZ) {
|
|
Base::Console().Error((std::string("SectionCut error: there is no ")
|
|
+ std::string(CutZName) + std::string("\n")).c_str());
|
|
return;
|
|
}
|
|
// refresh the Z cut limits according to the new bounding box of the cut result
|
|
// make the SectionCutZ invisible
|
|
CutFeatureZ->Visibility.setValue(false);
|
|
// make SectionCutX visible
|
|
CutObject->Visibility.setValue(true);
|
|
// get new bounding box
|
|
auto CutBoundingBox = getViewBoundingBox();
|
|
// refresh Z limits
|
|
refreshCutRanges(CutBoundingBox, Refresh::notXValue, Refresh::notYValue, Refresh::notZValue,
|
|
Refresh::notXRange, Refresh::notYRange, Refresh::ZRange);
|
|
// the value of Z can now be outside or at the limit, in this case reset the value too
|
|
if ((ui->cutZ->value() >= ui->cutZ->maximum())
|
|
|| (ui->cutZ->value() <= ui->cutZ->minimum()))
|
|
refreshCutRanges(CutBoundingBox, Refresh::notXValue, Refresh::notYValue, Refresh::ZValue,
|
|
Refresh::notXRange, Refresh::notYRange, Refresh::ZRange);
|
|
// make the SectionCutZ visible again
|
|
CutFeatureZ->Visibility.setValue(true);
|
|
// make SectionCutX invisible again
|
|
CutObject->Visibility.setValue(false);
|
|
// recompute the cut
|
|
Part::Cut* pcCutZ = static_cast<Part::Cut*>(CutFeatureZ);
|
|
pcCutZ->recomputeFeature(true);
|
|
}
|
|
else { // just Y
|
|
// refresh Z limits + values
|
|
auto CutBoundingBox = getViewBoundingBox();
|
|
refreshCutRanges(CutBoundingBox, Refresh::notXValue, Refresh::notYValue, Refresh::ZValue,
|
|
Refresh::notXRange, Refresh::notYRange, Refresh::ZRange);
|
|
// recompute the cut
|
|
Part::Cut* pcCut = dynamic_cast<Part::Cut*>(CutObject);
|
|
if (!pcCut) {
|
|
Base::Console().Error((std::string("SectionCut error: ") + std::string(CutZName)
|
|
+ std::string(" is no Part::Cut object. Cannot proceed.\n")).c_str());
|
|
return;
|
|
}
|
|
pcCut->recomputeFeature(true);
|
|
// refresh X limits
|
|
// this is done by
|
|
// first making the cut X box visible, the setting the limits only for X
|
|
// if x-limit in box direction is larger than object, reset value to saved limit
|
|
if (hasBoxX) {
|
|
auto CutBoxX = doc->getObject(BoxXName);
|
|
if (!CutBoxX)
|
|
return;
|
|
// first store the values
|
|
double storedX;
|
|
if (!ui->flipX->isChecked())
|
|
storedX = ui->cutX->minimum();
|
|
else
|
|
storedX = ui->cutX->maximum();
|
|
// show the cutting box
|
|
CutBoxX->Visibility.setValue(true);
|
|
// set new XRange
|
|
auto CutBoundingBox = getViewBoundingBox();
|
|
refreshCutRanges(CutBoundingBox, Refresh::notXValue, Refresh::notYValue, Refresh::notZValue,
|
|
Refresh::XRange, Refresh::notYRange, Refresh::notZRange);
|
|
// hide cutting box and compare resultwith stored value
|
|
CutBoxX->Visibility.setValue(false);
|
|
if (!ui->flipX->isChecked()) {
|
|
if (storedX > ui->cutX->minimum())
|
|
ui->cutX->setMinimum(storedX);
|
|
}
|
|
else {
|
|
if (storedX < ui->cutX->maximum())
|
|
ui->cutX->setMaximum(storedX);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SectionCut::onCutYHSsliderMoved(int val)
|
|
{
|
|
// we cannot cut to the edge because then the result is an empty shape
|
|
if (val == ui->cutYHS->maximum()) {
|
|
ui->cutYHS->setValue(ui->cutYHS->maximum() - ui->cutYHS->singleStep());
|
|
return;
|
|
}
|
|
if (val == ui->cutYHS->minimum()) {
|
|
ui->cutYHS->setValue(ui->cutYHS->minimum() + ui->cutYHS->singleStep());
|
|
return;
|
|
}
|
|
// the slider value is % of the cut range
|
|
double NewCutValue = ui->cutY->minimum()
|
|
+ val / 100.0 * (ui->cutY->maximum() - ui->cutY->minimum());
|
|
ui->cutYHS->setToolTip(QString::number(NewCutValue, 'g', Base::UnitsApi::getDecimals()));
|
|
ui->cutY->setValue(NewCutValue);
|
|
}
|
|
|
|
void SectionCut::onCutYHSChanged(int val)
|
|
{
|
|
onCutYHSsliderMoved(val);
|
|
}
|
|
|
|
void SectionCut::onCutZvalueChanged(double val)
|
|
{
|
|
CutValueHelper(val, ui->cutZ, ui->cutZHS);
|
|
|
|
auto CutBox = doc->getObject(BoxZName);
|
|
if (!CutBox)
|
|
return;
|
|
Part::Box* pcBox = dynamic_cast<Part::Box*>(CutBox);
|
|
if (!pcBox) {
|
|
Base::Console().Error((std::string("SectionCut error: ") + std::string(BoxZName)
|
|
+ std::string(" is no Part::Box object. Cannot proceed.\n")).c_str());
|
|
return;
|
|
}
|
|
Base::Placement placement = pcBox->Placement.getValue();
|
|
Base::Vector3d BoxPosition = placement.getPosition();
|
|
if (!ui->flipZ->isChecked())
|
|
BoxPosition.z = ui->cutZ->value() - pcBox->Height.getValue();
|
|
else //flipped
|
|
BoxPosition.z = ui->cutZ->value();
|
|
placement.setPosition(BoxPosition);
|
|
pcBox->Placement.setValue(placement);
|
|
|
|
auto CutObject = doc->getObject(CutZName);
|
|
if (!CutObject) {
|
|
Base::Console().Warning((std::string("SectionCut warning: there is no ")
|
|
+ std::string(CutZName) + std::string(", trying to recreate it\n")).c_str());
|
|
startCutting();
|
|
return;
|
|
}
|
|
Part::Cut* pcCut = dynamic_cast<Part::Cut*>(CutObject);
|
|
if (!pcCut) {
|
|
Base::Console().Error((std::string("SectionCut error: ") + std::string(CutZName)
|
|
+ std::string(" is no Part::Cut object. Cannot proceed.\n")).c_str());
|
|
return;
|
|
}
|
|
pcCut->recomputeFeature(true);
|
|
// refresh X and Y limits
|
|
// this is done e.g. for X by
|
|
// first making the cut X box visible, the setting the limits only for X
|
|
// if x-limit in box direction is larger than object, reset value to saved limit
|
|
SbBox3f CutBoundingBox;
|
|
if (hasBoxX) {
|
|
auto CutBoxX = doc->getObject(BoxXName);
|
|
if (!CutBoxX)
|
|
return;
|
|
// first store the values
|
|
double storedX;
|
|
if (!ui->flipX->isChecked())
|
|
storedX = ui->cutX->minimum();
|
|
else
|
|
storedX = ui->cutX->maximum();
|
|
// show the cutting box
|
|
CutBoxX->Visibility.setValue(true);
|
|
// set new XRange
|
|
CutBoundingBox = getViewBoundingBox();
|
|
refreshCutRanges(CutBoundingBox, Refresh::notXValue, Refresh::notYValue, Refresh::notZValue,
|
|
Refresh::XRange, Refresh::notYRange, Refresh::notZRange);
|
|
// hide cutting box and compare resultwith stored value
|
|
CutBoxX->Visibility.setValue(false);
|
|
if (!ui->flipX->isChecked()) {
|
|
if (storedX > ui->cutX->minimum())
|
|
ui->cutX->setMinimum(storedX);
|
|
}
|
|
else {
|
|
if (storedX < ui->cutX->maximum())
|
|
ui->cutX->setMaximum(storedX);
|
|
}
|
|
}
|
|
if (hasBoxY) {
|
|
auto CutBoxY = doc->getObject(BoxYName);
|
|
if (!CutBoxY)
|
|
return;
|
|
double storedY;
|
|
if (!ui->flipY->isChecked())
|
|
storedY = ui->cutY->minimum();
|
|
else
|
|
storedY = ui->cutY->maximum();
|
|
CutBoxY->Visibility.setValue(true);
|
|
CutBoundingBox = getViewBoundingBox();
|
|
refreshCutRanges(CutBoundingBox, Refresh::notXValue, Refresh::notYValue, Refresh::notZValue,
|
|
Refresh::notXRange, Refresh::YRange, Refresh::notZRange);
|
|
CutBoxY->Visibility.setValue(false);
|
|
if (!ui->flipY->isChecked()) {
|
|
if (storedY > ui->cutY->minimum())
|
|
ui->cutY->setMinimum(storedY);
|
|
}
|
|
else {
|
|
if (storedY < ui->cutY->maximum())
|
|
ui->cutY->setMaximum(storedY);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SectionCut::onCutZHSsliderMoved(int val)
|
|
{
|
|
// we cannot cut to the edge because then the result is an empty shape
|
|
if (val == ui->cutZHS->maximum()) {
|
|
ui->cutZHS->setValue(ui->cutZHS->maximum() - ui->cutZHS->singleStep());
|
|
return;
|
|
}
|
|
if (val == ui->cutZHS->minimum()) {
|
|
ui->cutZHS->setValue(ui->cutZHS->minimum() + ui->cutZHS->singleStep());
|
|
return;
|
|
}
|
|
// the slider value is % of the cut range
|
|
double NewCutValue = ui->cutZ->minimum()
|
|
+ val / 100.0 * (ui->cutZ->maximum() - ui->cutZ->minimum());
|
|
ui->cutZHS->setToolTip(QString::number(NewCutValue, 'g', Base::UnitsApi::getDecimals()));
|
|
ui->cutZ->setValue(NewCutValue);
|
|
}
|
|
|
|
void SectionCut::onCutZHSChanged(int val)
|
|
{
|
|
onCutZHSsliderMoved(val);
|
|
}
|
|
|
|
// helper function for the onFlip_clicked signal
|
|
void SectionCut::FlipClickedHelper(const char* BoxName)
|
|
{
|
|
// there might be no document
|
|
if (!Gui::Application::Instance->activeDocument()) {
|
|
noDocumentActions();
|
|
return;
|
|
}
|
|
// refresh objects and return in case the document was changed
|
|
if (doc != Gui::Application::Instance->activeDocument()->getDocument()) {
|
|
onRefreshCutPBclicked();
|
|
return;
|
|
}
|
|
// we must move the box e.g. in y-direction by its Width
|
|
auto CutBox = doc->getObject(BoxName);
|
|
// there should be a box, but maybe the user deleted it meanwhile
|
|
if (!CutBox) {
|
|
Base::Console().Warning((std::string("SectionCut warning: there is no ")
|
|
+ std::string(BoxName) + std::string(", trying to recreate it\n")).c_str());
|
|
// recreate the box
|
|
startCutting();
|
|
return;
|
|
}
|
|
Part::Box* pcBox = dynamic_cast<Part::Box*>(CutBox);
|
|
if (!pcBox) {
|
|
Base::Console().Error((std::string("SectionCut error: ") + std::string(BoxName)
|
|
+ std::string(" is no Part::Box object. Cannot proceed.\n")).c_str());
|
|
return;
|
|
}
|
|
// get its placement and size
|
|
Base::Placement placement = pcBox->Placement.getValue();
|
|
Base::Vector3d BoxPosition = placement.getPosition();
|
|
// flip the box
|
|
switch (std::string(BoxName).back())
|
|
{
|
|
case 'X':
|
|
if (ui->flipX->isChecked())
|
|
BoxPosition.x = BoxPosition.x + pcBox->Length.getValue();
|
|
else
|
|
BoxPosition.x = BoxPosition.x - pcBox->Length.getValue();
|
|
break;
|
|
case 'Y':
|
|
if (ui->flipY->isChecked())
|
|
BoxPosition.y = BoxPosition.y + pcBox->Width.getValue();
|
|
else
|
|
BoxPosition.y = BoxPosition.y - pcBox->Width.getValue();
|
|
break;
|
|
case 'Z':
|
|
if (ui->flipZ->isChecked())
|
|
BoxPosition.z = BoxPosition.z + pcBox->Height.getValue();
|
|
else
|
|
BoxPosition.z = BoxPosition.z - pcBox->Height.getValue();
|
|
break;
|
|
}
|
|
placement.setPosition(BoxPosition);
|
|
pcBox->Placement.setValue(placement);
|
|
}
|
|
|
|
void SectionCut::onFlipXclicked()
|
|
{
|
|
FlipClickedHelper(BoxXName);
|
|
|
|
auto CutObject = doc->getObject(CutXName);
|
|
// there should be a cut, but maybe the user deleted it meanwhile
|
|
if (!CutObject) {
|
|
Base::Console().Warning((std::string("SectionCut warning: there is no ")
|
|
+ std::string(CutXName) + std::string(", trying to recreate it\n")).c_str());
|
|
// recreate the box
|
|
startCutting();
|
|
return;
|
|
}
|
|
|
|
// if there is another cut, we must recalculate it too
|
|
// the hierarchy is always Z->Y->X
|
|
if (hasBoxY && !hasBoxZ) { // only Y
|
|
auto CutFeatureY = doc->getObject(CutYName);
|
|
if (!CutFeatureY) {
|
|
Base::Console().Warning(
|
|
(std::string("SectionCut warning: the expected ")
|
|
+ std::string(CutYName) + std::string(" is missing, trying to recreate it\n")).c_str());
|
|
// recreate the box
|
|
startCutting();
|
|
return;
|
|
}
|
|
Part::Cut* pcCutY = dynamic_cast<Part::Cut*>(CutFeatureY);
|
|
if (!pcCutY) {
|
|
Base::Console().Error((std::string("SectionCut error: ") + std::string(CutYName)
|
|
+ std::string(" is no Part::Cut object. Cannot proceed.\n")).c_str());
|
|
return;
|
|
}
|
|
pcCutY->recomputeFeature(true);
|
|
}
|
|
else if ((!hasBoxY && hasBoxZ) || (hasBoxY && hasBoxZ)) { // at least Z
|
|
// the main cut is Z, no matter if there is a cut in Y
|
|
auto CutFeatureZ = doc->getObject(CutZName);
|
|
if (!CutFeatureZ) {
|
|
Base::Console().Warning((std::string("SectionCut warning: the expected ")
|
|
+ std::string(CutZName) + std::string(" is missing, trying to recreate it\n")).c_str());
|
|
// recreate the box
|
|
startCutting();
|
|
return;
|
|
}
|
|
Part::Cut* pcCutZ = dynamic_cast<Part::Cut*>(CutFeatureZ);
|
|
if (!pcCutZ) {
|
|
Base::Console().Error((std::string("SectionCut error: ") + std::string(CutZName)
|
|
+ std::string(" is no Part::Cut object. Cannot proceed.\n")).c_str());
|
|
return;
|
|
}
|
|
pcCutZ->recomputeFeature(true);
|
|
}
|
|
else { // only do this when there is no other box to save recomputes
|
|
Part::Cut* pcCut = dynamic_cast<Part::Cut*>(CutObject);
|
|
if (!pcCut) {
|
|
Base::Console().Error((std::string("SectionCut error: ") + std::string(CutXName)
|
|
+ std::string(" is no Part::Cut object. Cannot proceed.\n")).c_str());
|
|
return;
|
|
}
|
|
pcCut->recomputeFeature(true);
|
|
}
|
|
}
|
|
|
|
void SectionCut::onFlipYclicked()
|
|
{
|
|
FlipClickedHelper(BoxYName);
|
|
|
|
auto CutObject = doc->getObject(CutYName);
|
|
// there should be a cut, but maybe the user deleted it meanwhile
|
|
if (!CutObject) {
|
|
Base::Console().Warning((std::string("SectionCut warning: there is no ")
|
|
+ std::string(CutYName) + std::string(", trying to recreate it\n")).c_str());
|
|
// recreate the box
|
|
startCutting();
|
|
return;
|
|
}
|
|
|
|
// if there is another cut, we must recalculate it too
|
|
// we only need to check for Z since the hierarchy is always Z->Y->X
|
|
if (hasBoxZ) {
|
|
auto CutFeatureZ = doc->getObject(CutZName);
|
|
Part::Cut* pcCutZ = dynamic_cast<Part::Cut*>(CutFeatureZ);
|
|
if (!pcCutZ) {
|
|
Base::Console().Error((std::string("SectionCut error: ") + std::string(CutZName)
|
|
+ std::string(" is no Part::Cut object. Cannot proceed.\n")).c_str());
|
|
return;
|
|
}
|
|
pcCutZ->recomputeFeature(true);
|
|
}
|
|
else {
|
|
Part::Cut* pcCut = dynamic_cast<Part::Cut*>(CutObject);
|
|
if (!pcCut) {
|
|
Base::Console().Error((std::string("SectionCut error: ") + std::string(CutYName)
|
|
+ std::string(" is no Part::Cut object. Cannot proceed.\n")).c_str());
|
|
return;
|
|
}
|
|
pcCut->recomputeFeature(true);
|
|
}
|
|
}
|
|
|
|
void SectionCut::onFlipZclicked()
|
|
{
|
|
FlipClickedHelper(BoxZName);
|
|
|
|
auto CutObject = doc->getObject(CutZName);
|
|
// there should be a cut, but maybe the user deleted it meanwhile
|
|
if (!CutObject) {
|
|
Base::Console().Warning((std::string("SectionCut warning: there is no ")
|
|
+ std::string(CutZName) + std::string(", trying to recreate it\n")).c_str());
|
|
// recreate the box
|
|
startCutting();
|
|
return;
|
|
}
|
|
Part::Cut* pcCut = dynamic_cast<Part::Cut*>(CutObject);
|
|
if (!pcCut) {
|
|
Base::Console().Error((std::string("SectionCut error: ") + std::string(CutZName)
|
|
+ std::string(" is no Part::Cut object. Cannot proceed.\n")).c_str());
|
|
return;
|
|
}
|
|
pcCut->recomputeFeature(true);
|
|
}
|
|
|
|
// changes the cutface color
|
|
void SectionCut::onCutColorclicked()
|
|
{
|
|
// re-cut to change the color of all cut boxes
|
|
if (ui->groupBoxX->isChecked() || ui->groupBoxY->isChecked() || ui->groupBoxZ->isChecked())
|
|
startCutting();
|
|
}
|
|
|
|
void SectionCut::onTransparencySliderMoved(int val)
|
|
{
|
|
ui->CutTransparency->setToolTip(QString::number(val) + QString::fromLatin1(" %"));
|
|
// highlight the tooltip
|
|
QToolTip::showText(QCursor::pos(), QString::number(val) + QString::fromLatin1(" %"), nullptr);
|
|
// re-cut to change the color of all cut boxes
|
|
if (ui->groupBoxX->isChecked() || ui->groupBoxY->isChecked() || ui->groupBoxZ->isChecked())
|
|
startCutting();
|
|
}
|
|
|
|
void SectionCut::onTransparencyChanged(int val)
|
|
{
|
|
onTransparencySliderMoved(val);
|
|
}
|
|
|
|
// refreshes the list of document objects and the visible objects
|
|
void SectionCut::onRefreshCutPBclicked()
|
|
{
|
|
// get document
|
|
auto docGui = Gui::Application::Instance->activeDocument();
|
|
if (!docGui) {
|
|
Base::Console().Error("SectionCut error: there is no document\n");
|
|
return;
|
|
}
|
|
doc = docGui->getDocument();
|
|
// get all objects in the document
|
|
std::vector<App::DocumentObject*> ObjectsList = doc->getObjects();
|
|
if (ObjectsList.empty()) {
|
|
Base::Console().Error("SectionCut error: there are no objects in the document\n");
|
|
return;
|
|
}
|
|
// 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.push_back(*it);
|
|
}
|
|
}
|
|
// reset defaults
|
|
hasBoxX = false;
|
|
hasBoxY = false;
|
|
hasBoxZ = false;
|
|
// we can have existing cuts
|
|
if (doc->getObject(CutZName)) {
|
|
hasBoxZ = true;
|
|
ui->groupBoxZ->blockSignals(true);
|
|
ui->groupBoxZ->setChecked(true);
|
|
ui->groupBoxZ->blockSignals(false);
|
|
}
|
|
if (doc->getObject(CutYName)) {
|
|
hasBoxY = true;
|
|
ui->groupBoxY->blockSignals(true);
|
|
ui->groupBoxY->setChecked(true);
|
|
ui->groupBoxY->blockSignals(false);
|
|
}
|
|
if (doc->getObject(CutXName)) {
|
|
hasBoxX = true;
|
|
ui->groupBoxX->blockSignals(true);
|
|
ui->groupBoxX->setChecked(true);
|
|
ui->groupBoxX->blockSignals(false);
|
|
}
|
|
// if there is a cut, disable the button
|
|
if (hasBoxX || hasBoxY || hasBoxZ)
|
|
ui->RefreshCutPB->setEnabled(false);
|
|
}
|
|
|
|
SbBox3f SectionCut::getViewBoundingBox()
|
|
{
|
|
SbBox3f Box;
|
|
auto docGui = Gui::Application::Instance->activeDocument();
|
|
if (!docGui) {
|
|
Base::Console().Error("SectionCut error: there is no active document\n");
|
|
return Box; // return an empty box
|
|
}
|
|
Gui::View3DInventor* view = dynamic_cast<Gui::View3DInventor*>(docGui->getActiveView());
|
|
if (!view) {
|
|
Base::Console().Error("SectionCut error: could not get the active view\n");
|
|
return Box; // return an empty box
|
|
}
|
|
Gui::View3DInventorViewer* viewer = view->getViewer();
|
|
SoCamera* camera = viewer->getSoRenderManager()->getCamera();
|
|
if (!camera || !camera->isOfType(SoOrthographicCamera::getClassTypeId()))
|
|
return Box; // return an empty box
|
|
// get scene bounding box
|
|
SoGetBoundingBoxAction action(viewer->getSoRenderManager()->getViewportRegion());
|
|
action.apply(viewer->getSceneGraph());
|
|
return action.getBoundingBox();
|
|
}
|
|
|
|
void SectionCut::refreshCutRanges(SbBox3f BoundingBox,
|
|
bool forXValue, bool forYValue, bool forZValue,
|
|
bool forXRange, bool forYRange, bool forZRange)
|
|
{
|
|
if (!BoundingBox.isEmpty()) {
|
|
SbVec3f center = BoundingBox.getCenter();
|
|
int minDecimals = Base::UnitsApi::getDecimals();
|
|
float lenx, leny, lenz;
|
|
BoundingBox.getSize(lenx, leny, lenz);
|
|
int steps = 100;
|
|
|
|
// set the ranges
|
|
float rangeMin; // to silence a compiler warning we use a float
|
|
float rangeMax;
|
|
if (forXRange) {
|
|
rangeMin = center[0] - (lenx / 2);
|
|
rangeMax = center[0] + (lenx / 2);
|
|
ui->cutX->setRange(rangeMin, rangeMax);
|
|
// determine the single step values
|
|
lenx = lenx / steps;
|
|
int dim = static_cast<int>(log10(lenx));
|
|
double singleStep = pow(10.0, dim);
|
|
ui->cutX->setSingleStep(singleStep);
|
|
}
|
|
if (forYRange) {
|
|
rangeMin = center[1] - (leny / 2);
|
|
rangeMax = center[1] + (leny / 2);
|
|
ui->cutY->setRange(rangeMin, rangeMax);
|
|
leny = leny / steps;
|
|
int dim = static_cast<int>(log10(leny));
|
|
double singleStep = pow(10.0, dim);
|
|
ui->cutY->setSingleStep(singleStep);
|
|
}
|
|
if (forZRange) {
|
|
rangeMin = center[2] - (lenz / 2);
|
|
rangeMax = center[2] + (lenz / 2);
|
|
ui->cutZ->setRange(rangeMin, rangeMax);
|
|
lenz = lenz / steps;
|
|
int dim = static_cast<int>(log10(lenz));
|
|
double singleStep = pow(10.0, dim);
|
|
ui->cutZ->setSingleStep(singleStep);
|
|
}
|
|
if (forXValue) {
|
|
ui->cutX->setValue(center[0]);
|
|
ui->cutXHS->setValue(50);
|
|
}
|
|
if (forYValue) {
|
|
ui->cutY->setValue(center[1]);
|
|
ui->cutYHS->setValue(50);
|
|
}
|
|
if (forZValue) {
|
|
ui->cutZ->setValue(center[2]);
|
|
ui->cutZHS->setValue(50);
|
|
}
|
|
|
|
// set decimals
|
|
ui->cutX->setDecimals(minDecimals);
|
|
ui->cutY->setDecimals(minDecimals);
|
|
ui->cutZ->setDecimals(minDecimals);
|
|
}
|
|
}
|
|
|
|
#include "moc_SectionCutting.cpp"
|