/*************************************************************************** * Copyright (c) 2013 Jan Rheinlaender * * * * * * 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 * * . * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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(obj)) { geo->setMaterialAppearance(ShapeAppearance[0]); } ViewProviderGeometryObject::attach(obj); // TODO remove this field (2015-09-08, Fat-Zer) App::DocumentObject* o = getObject(); if (o->is()) { datumType = QStringLiteral("Plane"); datumText = QObject::tr("Plane"); datumMenuText = tr("Datum Plane Parameters"); } else if (o->is()) { datumType = QStringLiteral("Line"); datumText = QObject::tr("Line"); datumMenuText = tr("Datum Line Parameters"); } else if (o->is()) { datumType = QStringLiteral("Point"); datumText = QObject::tr("Point"); datumMenuText = tr("Datum Point Parameters"); } else if (o->is()) { 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&) { // 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 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(detail); element = line_detail->getLineIndex(); } else if (detail->getTypeId() == SoFaceDetail::getClassTypeId()) { const SoFaceDetail* face_detail = static_cast(detail); element = face_detail->getFaceIndex(); } else if (detail->getTypeId() == SoPointDetail::getClassTypeId()) { const SoPointDetail* point_detail = static_cast(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(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(); PartDesign::Body* activeBody = activeView->getActiveObject(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 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(); if (ext) { objs = ext->getObjects(); } } else { // Fallback to whole document objs = this->getObject()->getDocument()->getObjects(); } } Gui::View3DInventor* view = dynamic_cast(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& 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(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; } }