414 lines
15 KiB
C++
414 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 "PreCompiled.h"
|
|
|
|
#ifndef _PreComp_
|
|
# 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>
|
|
#endif
|
|
|
|
#include <App/Document.h>
|
|
#include <App/DocumentObjectGroup.h>
|
|
#include <Gui/Application.h>
|
|
#include <Gui/Command.h>
|
|
#include <Gui/Control.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 );
|
|
|
|
App::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;
|
|
msgBox.setText(QObject::tr("A dialog is already open in the task panel"));
|
|
msgBox.setInformativeText(QObject::tr("Do you want to 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;
|
|
}
|