Files
create/src/Mod/PartDesign/Gui/ViewProviderDatum.cpp
2025-11-11 13:49:01 +01:00

457 lines
15 KiB
C++

/***************************************************************************
* Copyright (c) 2013 Jan Rheinlaender *
* <jrheinlaender@users.sourceforge.net> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
***************************************************************************/
#include <Inventor/actions/SoGetBoundingBoxAction.h>
#include <Inventor/details/SoFaceDetail.h>
#include <Inventor/details/SoLineDetail.h>
#include <Inventor/details/SoPointDetail.h>
#include <Inventor/nodes/SoDrawStyle.h>
#include <Inventor/nodes/SoMaterial.h>
#include <Inventor/nodes/SoMaterialBinding.h>
#include <Inventor/nodes/SoPickStyle.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/nodes/SoShapeHints.h>
#include <Precision.hxx>
#include <QMessageBox>
#include <QAction>
#include <QMenu>
#include <App/Document.h>
#include <App/DocumentObjectGroup.h>
#include <Gui/Application.h>
#include <Gui/Command.h>
#include <Gui/Control.h>
#include <Gui/MainWindow.h>
#include <Gui/View3DInventor.h>
#include <Gui/View3DInventorViewer.h>
#include <Gui/ViewProviderCoordinateSystem.h>
#include <Mod/PartDesign/App/Body.h>
#include <Mod/PartDesign/App/DatumCS.h>
#include <Mod/PartDesign/App/DatumLine.h>
#include <Mod/PartDesign/App/DatumPlane.h>
#include <Mod/PartDesign/App/DatumPoint.h>
#include "ViewProviderDatum.h"
#include "TaskDatumParameters.h"
#include "Utils.h"
#include "ViewProviderBody.h"
using namespace PartDesignGui;
PROPERTY_SOURCE_WITH_EXTENSIONS(PartDesignGui::ViewProviderDatum, Gui::ViewProviderGeometryObject)
// static data
const double ViewProviderDatum::defaultSize = Gui::ViewProviderCoordinateSystem::defaultSize();
ViewProviderDatum::ViewProviderDatum()
{
PartGui::ViewProviderAttachExtension::initExtension(this);
pShapeSep = new SoSeparator();
pShapeSep->ref();
pPickStyle = new SoPickStyle();
pPickStyle->ref();
DisplayMode.setStatus(App::Property::Hidden, true);
// set default color for datums (golden yellow with 60% transparency)
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Mod/PartDesign"
);
unsigned long shcol = hGrp->GetUnsigned("DefaultDatumColor", 0xFFD70099);
Base::Color col((uint32_t)shcol);
ShapeAppearance.setDiffuseColor(col);
Transparency.setValue(col.a * 100);
oldWb = "";
oldTip = nullptr;
}
ViewProviderDatum::~ViewProviderDatum()
{
pShapeSep->unref();
pPickStyle->unref();
}
void ViewProviderDatum::attach(App::DocumentObject* obj)
{
if (auto geo = dynamic_cast<App::GeoFeature*>(obj)) {
geo->setMaterialAppearance(ShapeAppearance[0]);
}
ViewProviderGeometryObject::attach(obj);
// TODO remove this field (2015-09-08, Fat-Zer)
App::DocumentObject* o = getObject();
if (o->is<PartDesign::Plane>()) {
datumType = QStringLiteral("Plane");
datumText = QObject::tr("Plane");
datumMenuText = tr("Datum Plane Parameters");
}
else if (o->is<PartDesign::Line>()) {
datumType = QStringLiteral("Line");
datumText = QObject::tr("Line");
datumMenuText = tr("Datum Line Parameters");
}
else if (o->is<PartDesign::Point>()) {
datumType = QStringLiteral("Point");
datumText = QObject::tr("Point");
datumMenuText = tr("Datum Point Parameters");
}
else if (o->is<PartDesign::CoordinateSystem>()) {
datumType = QStringLiteral("CoordinateSystem");
datumText = QObject::tr("Coordinate System");
datumMenuText = tr("Local Coordinate System Parameters");
}
SoShapeHints* hints = new SoShapeHints();
hints->shapeType.setValue(SoShapeHints::UNKNOWN_SHAPE_TYPE);
hints->vertexOrdering.setValue(SoShapeHints::COUNTERCLOCKWISE);
SoDrawStyle* fstyle = new SoDrawStyle();
fstyle->style = SoDrawStyle::FILLED;
fstyle->lineWidth = 3;
fstyle->pointSize = 5;
pPickStyle->style = SoPickStyle::SHAPE;
SoMaterialBinding* matBinding = new SoMaterialBinding;
matBinding->value = SoMaterialBinding::OVERALL;
SoSeparator* sep = new SoSeparator();
sep->addChild(hints);
sep->addChild(fstyle);
sep->addChild(pPickStyle);
sep->addChild(matBinding);
sep->addChild(pcShapeMaterial);
sep->addChild(pShapeSep);
addDisplayMaskMode(sep, "Base");
}
bool ViewProviderDatum::onDelete(const std::vector<std::string>&)
{
// TODO: Ask user what to do about dependent objects, e.g. Sketches that have this feature as
// their support
// 1. Delete
// 2. Suppress
// 3. Re-route
return true;
}
std::vector<std::string> ViewProviderDatum::getDisplayModes() const
{
return {"Base"};
}
void ViewProviderDatum::setDisplayMode(const char* ModeName)
{
if (strcmp(ModeName, "Base") == 0) {
setDisplayMaskMode("Base");
}
ViewProviderGeometryObject::setDisplayMode(ModeName);
}
std::string ViewProviderDatum::getElement(const SoDetail* detail) const
{
if (detail) {
int element = 1;
if (detail->getTypeId() == SoLineDetail::getClassTypeId()) {
const SoLineDetail* line_detail = static_cast<const SoLineDetail*>(detail);
element = line_detail->getLineIndex();
}
else if (detail->getTypeId() == SoFaceDetail::getClassTypeId()) {
const SoFaceDetail* face_detail = static_cast<const SoFaceDetail*>(detail);
element = face_detail->getFaceIndex();
}
else if (detail->getTypeId() == SoPointDetail::getClassTypeId()) {
const SoPointDetail* point_detail = static_cast<const SoPointDetail*>(detail);
element = point_detail->getCoordinateIndex();
}
if (element == 0) {
return datumType.toStdString();
}
}
return {};
}
SoDetail* ViewProviderDatum::getDetail(const char* subelement) const
{
QString subelem = QString::fromLatin1(subelement);
if (subelem == QObject::tr("Line")) {
SoLineDetail* detail = new SoLineDetail();
detail->setPartIndex(0);
return detail;
}
else if (subelem == QObject::tr("Plane")) {
SoFaceDetail* detail = new SoFaceDetail();
detail->setPartIndex(0);
return detail;
}
else if (subelem == QObject::tr("Point")) {
SoPointDetail* detail = new SoPointDetail();
detail->setCoordinateIndex(0);
return detail;
}
return nullptr;
}
bool ViewProviderDatum::isSelectable() const
{
return true;
}
void ViewProviderDatum::setupContextMenu(QMenu* menu, QObject* receiver, const char* member)
{
QAction* act;
act = menu->addAction(QObject::tr("Edit Datum"), receiver, member);
act->setData(QVariant((int)ViewProvider::Default));
// Call the extensions
Gui::ViewProvider::setupContextMenu(menu, receiver, member);
}
bool ViewProviderDatum::setEdit(int ModNum)
{
if (!ViewProvider::setEdit(ModNum)) {
return false;
}
// TODO Share this code with Features view providers somehow (2015-09-08, Fat-Zer)
if (ModNum == ViewProvider::Default) {
// When double-clicking on the item for this datum feature the
// object unsets and sets its edit mode without closing
// the task panel
Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog();
TaskDlgDatumParameters* datumDlg = qobject_cast<TaskDlgDatumParameters*>(dlg);
if (datumDlg && datumDlg->getViewProvider() != this) {
datumDlg = nullptr; // another datum feature left open its task panel
}
if (dlg && !datumDlg) {
QMessageBox msgBox(Gui::getMainWindow());
msgBox.setText(QObject::tr("A dialog is already open in the task panel"));
msgBox.setInformativeText(QObject::tr("Close this dialog?"));
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::Yes);
int ret = msgBox.exec();
if (ret == QMessageBox::Yes) {
Gui::Control().closeDialog();
}
else {
return false;
}
}
// clear the selection (convenience)
Gui::Selection().clearSelection();
oldWb = Gui::Command::assureWorkbench("PartDesignWorkbench");
// start the edit dialog
if (datumDlg) {
Gui::Control().showDialog(datumDlg);
}
else {
Gui::Control().showDialog(new TaskDlgDatumParameters(this));
}
return true;
}
else {
return ViewProvider::setEdit(ModNum);
}
}
bool ViewProviderDatum::doubleClicked()
{
auto activeDoc = Gui::Application::Instance->activeDocument();
if (!activeDoc) {
activeDoc = getDocument();
}
auto activeView = activeDoc->getActiveView();
if (!activeView) {
return false;
}
std::string Msg("Edit ");
Msg += this->pcObject->Label.getValue();
Gui::Command::openCommand(Msg.c_str());
Part::Datum* pcDatum = getObject<Part::Datum>();
PartDesign::Body* activeBody = activeView->getActiveObject<PartDesign::Body*>(PDBODYKEY);
auto datumBody = PartDesignGui::getBodyFor(pcDatum, false);
if (datumBody) {
if (datumBody != activeBody) {
Gui::Command::doCommand(
Gui::Command::Gui,
"Gui.ActiveDocument.ActiveView.setActiveObject('%s',%s)",
PDBODYKEY,
Gui::Command::getObjectCmd(datumBody).c_str()
);
activeBody = datumBody;
}
}
return PartDesignGui::setEdit(pcObject, activeBody);
}
void ViewProviderDatum::unsetEdit(int ModNum)
{
// return to the WB we were in before editing the PartDesign feature
Gui::Command::assureWorkbench(oldWb.c_str());
if (ModNum == ViewProvider::Default) {
// when pressing ESC make sure to close the dialog
Gui::Control().closeDialog();
}
else {
Gui::ViewProviderGeometryObject::unsetEdit(ModNum);
}
}
void ViewProviderDatum::updateExtents()
{
setExtents(getRelevantBoundBox());
}
void ViewProviderDatum::setExtents(const SbBox3f& bbox)
{
const SbVec3f& min = bbox.getMin();
const SbVec3f& max = bbox.getMax();
setExtents(
Base::BoundBox3d(
min.getValue()[0],
min.getValue()[1],
min.getValue()[2],
max.getValue()[0],
max.getValue()[1],
max.getValue()[2]
)
);
}
SbBox3f ViewProviderDatum::getRelevantBoundBox() const
{
std::vector<App::DocumentObject*> objs;
// Probe body first
PartDesign::Body* body = PartDesign::Body::findBodyOf(this->getObject());
if (body) {
objs = body->getFullModel();
}
else {
// Probe if we belongs to some group
App::DocumentObject* group = App::DocumentObjectGroup::getGroupOfObject(this->getObject());
if (group) {
auto* ext = group->getExtensionByType<App::GroupExtension>();
if (ext) {
objs = ext->getObjects();
}
}
else {
// Fallback to whole document
objs = this->getObject()->getDocument()->getObjects();
}
}
Gui::View3DInventor* view = dynamic_cast<Gui::View3DInventor*>(this->getActiveView());
if (view) {
Gui::View3DInventorViewer* viewer = view->getViewer();
SoGetBoundingBoxAction bboxAction(viewer->getSoRenderManager()->getViewportRegion());
SbBox3f bbox = getRelevantBoundBox(bboxAction, objs);
if (bbox.getVolume() < Precision::Confusion()) {
bbox.extendBy(defaultBoundBox());
}
return bbox;
}
else {
return defaultBoundBox();
}
}
SbBox3f ViewProviderDatum::getRelevantBoundBox(
SoGetBoundingBoxAction& bboxAction,
const std::vector<App::DocumentObject*>& objs
)
{
SbBox3f bbox = defaultBoundBox();
// Adds the bbox of given feature to the output
for (auto obj : objs) {
ViewProvider* vp = Gui::Application::Instance->getViewProvider(obj);
if (!vp) {
continue;
}
if (!vp->isVisible()) {
continue;
}
if (obj->isDerivedFrom(Part::Datum::getClassTypeId())) {
// Treat datums only as their basepoint
// I hope it's ok to take FreeCAD's point here
Base::Vector3d basePoint = static_cast<Part::Datum*>(obj)->getBasePoint();
bbox.extendBy(SbVec3f(basePoint.x, basePoint.y, basePoint.z));
}
else {
bboxAction.apply(vp->getRoot());
SbBox3f obj_bbox = bboxAction.getBoundingBox();
if (obj_bbox.getVolume() < Precision::Infinite()) {
bbox.extendBy(obj_bbox);
}
}
}
// TODO: shrink bbox when all other elements are too small
return bbox;
}
SbBox3f ViewProviderDatum::defaultBoundBox()
{
return SbBox3f(-defaultSize, -defaultSize, -defaultSize, defaultSize, defaultSize, defaultSize);
}
bool ViewProviderDatum::isPickable()
{
return bool(pPickStyle->style.getValue() == SoPickStyle::SHAPE);
}
void ViewProviderDatum::setPickable(bool val)
{
if (val) {
pPickStyle->style = SoPickStyle::SHAPE;
}
else {
pPickStyle->style = SoPickStyle::UNPICKABLE;
}
}