diff --git a/src/App/DocumentObject.h b/src/App/DocumentObject.h index bcda2a2481..29032008c0 100644 --- a/src/App/DocumentObject.h +++ b/src/App/DocumentObject.h @@ -66,7 +66,6 @@ enum ObjectStatus { NoAutoExpand = 17, // disable tree item auto expand on selection for this object PendingTransactionUpdate = 18, // mark that the object expects a call to onUndoRedoFinished() after transaction is finished. RecomputeExtension = 19, // mark the object to recompute its extensions - TouchOnColorChange = 20, // inform view provider touch object on color change }; /** Return object for feature execution diff --git a/src/Gui/TaskElementColors.cpp b/src/Gui/TaskElementColors.cpp index e36b6c03fe..a1f535ff8b 100644 --- a/src/Gui/TaskElementColors.cpp +++ b/src/Gui/TaskElementColors.cpp @@ -194,25 +194,6 @@ public: } void accept() { - if(touched) { - touched = false; - if(ui->recompute->isChecked()) { - auto obj = vp->getObject(); - auto objs = obj->getInListRecursive(); - objs.push_back(obj); - for (auto o : objs) - o->enforceRecompute(); - obj->getDocument()->recompute(objs); - } else { - for (auto obj : App::Document::getDependencyList( - {vp->getObject()}, App::Document::DepSort)) - { - auto vp = Application::Instance->getViewProvider(obj); - if (vp) - vp->updateColors(); - } - } - } App::GetApplication().closeActiveTransaction(); } @@ -390,7 +371,6 @@ ElementColors::ElementColors(ViewProviderDocumentObject* vp, bool noHide) if(noHide) d->ui->hideSelection->setVisible(false); - d->ui->recompute->setChecked(ViewParams::instance()->getColorRecompute()); d->ui->onTop->setChecked(ViewParams::instance()->getColorOnTop()); Selection().addSelectionGate(d, ResolveMode::NoResolve); @@ -410,10 +390,6 @@ ElementColors::~ElementColors() Selection().rmvSelectionGate(); } -void ElementColors::on_recompute_clicked(bool checked) { - ViewParams::instance()->setColorRecompute(checked); -} - void ElementColors::on_onTop_clicked(bool checked) { ViewParams::instance()->setColorOnTop(checked); d->vpParent->OnTopWhenSelected.setValue(checked?3:d->onTopMode); diff --git a/src/Gui/TaskElementColors.h b/src/Gui/TaskElementColors.h index 34bb5b122b..85b2e7ef5f 100644 --- a/src/Gui/TaskElementColors.h +++ b/src/Gui/TaskElementColors.h @@ -51,7 +51,6 @@ private Q_SLOTS: void on_elementList_itemDoubleClicked(QListWidgetItem *item); void on_elementList_itemSelectionChanged(); void on_elementList_itemEntered(QListWidgetItem *item); - void on_recompute_clicked(bool checked); void on_onTop_clicked(bool checked); void on_hideSelection_clicked(); void on_boxSelect_clicked(); diff --git a/src/Gui/TaskElementColors.ui b/src/Gui/TaskElementColors.ui index 08235f680d..96a5294196 100644 --- a/src/Gui/TaskElementColors.ui +++ b/src/Gui/TaskElementColors.ui @@ -72,13 +72,6 @@ - - - Recompute after commit - - - - On-top when selected diff --git a/src/Gui/ViewParams.h b/src/Gui/ViewParams.h index 020403ea71..88b1331c41 100644 --- a/src/Gui/ViewParams.h +++ b/src/Gui/ViewParams.h @@ -63,7 +63,6 @@ public: FC_VIEW_PARAM(ShowSelectionBoundingBox,bool,Bool,false) \ FC_VIEW_PARAM(PropertyViewTimer, unsigned long, Unsigned, 100) \ FC_VIEW_PARAM(ColorOnTop, bool, Bool, true) \ - FC_VIEW_PARAM(ColorRecompute, bool, Bool, true) \ #undef FC_VIEW_PARAM #define FC_VIEW_PARAM(_name,_ctype,_type,_def) \ diff --git a/src/Gui/ViewProviderDocumentObject.cpp b/src/Gui/ViewProviderDocumentObject.cpp index a4985fa897..61ae46e45c 100644 --- a/src/Gui/ViewProviderDocumentObject.cpp +++ b/src/Gui/ViewProviderDocumentObject.cpp @@ -54,6 +54,58 @@ FC_LOG_LEVEL_INIT("Gui", true, true) using namespace Gui; +/////////////////////////////////////////////////////////////////////////// +// +static int _ColorUpdateCounter; +static std::set _ColorChangedObjects; + +ColorUpdater::ColorUpdater() +{ + if (_ColorUpdateCounter >= 0) + ++_ColorUpdateCounter; +} + +ColorUpdater::~ColorUpdater() +{ + if (_ColorUpdateCounter <= 0 + || --_ColorUpdateCounter > 0 + || _ColorChangedObjects.empty()) + return; + // To prevent infinite recursive update + _ColorUpdateCounter = -1; + try { + std::set inset; + for (const auto &objT : _ColorChangedObjects) { + if (auto obj = objT.getObject()) { + if (!obj->isRecomputing() && !obj->isTouched()) + obj->getInListEx(inset, true); + } + } + std::vector objs(inset.begin(), inset.end()); + for (auto obj : App::Document::getDependencyList(objs, App::Document::DepSort)) { + if (obj->isRecomputing() || obj->isTouched() || !inset.count(obj)) + continue; + if (auto vp = Base::freecad_dynamic_cast( + Gui::Application::Instance->getViewProvider(obj))) { + vp->checkColorUpdate(); + } + } + } catch (Base::Exception &e) { + e.ReportException(); + } catch (...) + {} + _ColorChangedObjects.clear(); + _ColorUpdateCounter = 0; +} + +void ColorUpdater::addObject(App::DocumentObject *obj) +{ + if (_ColorUpdateCounter && obj && !obj->isRecomputing() && !obj->isTouched()) + _ColorChangedObjects.emplace(obj); +} + + +/////////////////////////////////////////////////////////////////////////////////// PROPERTY_SOURCE(Gui::ViewProviderDocumentObject, Gui::ViewProvider) @@ -97,16 +149,12 @@ void ViewProviderDocumentObject::getTaskViewContent(std::vector(); - for(Gui::ViewProviderExtension* ext : vector) - ext->extensionStartRestoring(); + callExtension(&ViewProviderExtension::extensionStartRestoring); } void ViewProviderDocumentObject::finishRestoring() { - auto vector = getExtensionsDerivedFromType(); - for(Gui::ViewProviderExtension* ext : vector) - ext->extensionFinishRestoring(); + callExtension(&ViewProviderExtension::extensionFinishRestoring); } bool ViewProviderDocumentObject::isAttachedToDocument() const @@ -278,6 +326,8 @@ void ViewProviderDocumentObject::addDefaultAction(QMenu* menu, const QString& te void ViewProviderDocumentObject::setModeSwitch() { if(isShowable()) ViewProvider::setModeSwitch(); + else + callExtension(&ViewProviderExtension::extensionModeSwitchChange); } void ViewProviderDocumentObject::show(void) @@ -357,15 +407,12 @@ void ViewProviderDocumentObject::attach(App::DocumentObject *pcObj) } //attach the extensions - auto vector = getExtensionsDerivedFromType(); - for (Gui::ViewProviderExtension* ext : vector) - ext->extensionAttach(pcObj); + callExtension(&ViewProviderExtension::extensionAttach,pcObj); } void ViewProviderDocumentObject::reattach(App::DocumentObject *pcObj) { - auto vector = getExtensionsDerivedFromType(); - for (Gui::ViewProviderExtension* ext : vector) - ext->extensionReattach(pcObj); + setStatus(Detach, false); + callExtension(&ViewProviderExtension::extensionReattach,pcObj); } void ViewProviderDocumentObject::update(const App::Property* prop) @@ -505,11 +552,8 @@ PyObject* ViewProviderDocumentObject::getPyObject() bool ViewProviderDocumentObject::canDropObjectEx(App::DocumentObject* obj, App::DocumentObject *owner, const char *subname, const std::vector &elements) const { - auto vector = getExtensionsDerivedFromType(); - for(Gui::ViewProviderExtension* ext : vector){ - if(ext->extensionCanDropObjectEx(obj,owner,subname,elements)) - return true; - } + if(queryExtension(&ViewProviderExtension::extensionCanDropObjectEx,obj,owner,subname,elements)) + return true; if(obj && obj->getDocument()!=getObject()->getDocument()) return false; return canDropObject(obj); @@ -583,10 +627,8 @@ bool ViewProviderDocumentObject::getElementPicked(const SoPickedPoint *pp, std:: { if(!isSelectable()) return false; - auto vector = getExtensionsDerivedFromType(); - for(Gui::ViewProviderExtension* ext : vector) - if(ext->extensionGetElementPicked(pp,subname)) - return true; + if(queryExtension(&ViewProviderExtension::extensionGetElementPicked,pp,subname)) + return true; auto childRoot = getChildRoot(); int idx; diff --git a/src/Gui/ViewProviderDocumentObject.h b/src/Gui/ViewProviderDocumentObject.h index fe942da333..0ee96a8407 100644 --- a/src/Gui/ViewProviderDocumentObject.h +++ b/src/Gui/ViewProviderDocumentObject.h @@ -227,6 +227,20 @@ private: }; +/** Convenient class to auto update colors for all affect objects + */ +class GuiExport ColorUpdater +{ +public: + ColorUpdater(); + ~ColorUpdater(); + static void addObject(App::DocumentObject *obj); +private: + /// Private new operator to prevent heap allocation + void* operator new(size_t size); +}; + + } // namespace Gui #endif // GUI_VIEWPROVIDER_DOCUMENTOBJECT_H diff --git a/src/Mod/Part/App/AppPartPy.cpp b/src/Mod/Part/App/AppPartPy.cpp index fce8f01264..e5351f84f6 100644 --- a/src/Mod/Part/App/AppPartPy.cpp +++ b/src/Mod/Part/App/AppPartPy.cpp @@ -872,17 +872,30 @@ private: { PyObject *pcObj = nullptr; char *name = "Shape"; - if (!PyArg_ParseTuple(args.ptr(), "O!|s", &(TopoShapePy::Type), &pcObj, &name)) + if (!PyArg_ParseTuple(args.ptr(), "O|s", &pcObj, &name)) throw Py::Exception(); App::Document *pcDoc = App::GetApplication().getActiveDocument(); if (!pcDoc) pcDoc = App::GetApplication().newDocument(); - TopoShapePy* pShape = static_cast(pcObj); + App::Document *pcSrcDoc = nullptr; + TopoShape shape; + if (PyObject_TypeCheck(pcObj, &TopoShapePy::Type)) + shape = *static_cast(pcObj)->getTopoShapePtr(); + else if (PyObject_TypeCheck(pcObj, &GeometryPy::Type)) + shape = static_cast(pcObj)->getGeometryPtr()->toShape(); + else if (PyObject_TypeCheck(pcObj, &App::DocumentObjectPy::Type)) { + auto obj = static_cast(pcObj)->getDocumentObjectPtr(); + pcSrcDoc = obj->getDocument(); + shape = Feature::getTopoShape(obj); + } + else + throw Py::TypeError("Expects argument of type DocumentObject, Shape, or Geometry"); Part::Feature *pcFeature = static_cast(pcDoc->addObject("Part::Feature", name)); // copy the data - pcFeature->Shape.setValue(*pShape->getTopoShapePtr()); - pcDoc->recompute(); + pcFeature->Shape.setValue(shape); + pcFeature->signalMapShapeColors(pcSrcDoc); + pcFeature->purgeTouched(); return Py::asObject(pcFeature->getPyObject()); } Py::Object getFacets(const Py::Tuple& args) diff --git a/src/Mod/Part/App/PartFeature.cpp b/src/Mod/Part/App/PartFeature.cpp index bf40f2bc3f..5c5c6dcf5b 100644 --- a/src/Mod/Part/App/PartFeature.cpp +++ b/src/Mod/Part/App/PartFeature.cpp @@ -95,6 +95,8 @@ PROPERTY_SOURCE(Part::Feature, App::GeoFeature) Feature::Feature(void) { ADD_PROPERTY(Shape, (TopoDS_Shape())); + ADD_PROPERTY_TYPE(ColoredElements, (0), "", + (App::PropertyType)(App::Prop_Hidden|App::Prop_ReadOnly|App::Prop_Output),""); } Feature::~Feature() @@ -1304,6 +1306,21 @@ const std::vector& Feature::getElementTypes(bool all) const return res; } +Feature *Feature::create(const TopoShape &s, const char *name, App::Document *doc) +{ + if (!name || !name[0]) + name = "Shape"; + if (!doc) { + doc = App::GetApplication().getActiveDocument(); + if (!doc) + doc = App::GetApplication().newDocument(); + } + auto res = static_cast(doc->addObject("Part::Feature", name)); + res->Shape.setValue(s); + res->purgeTouched(); + return res; +} + static inline App::PropertyBool *propDisableMapping(App::PropertyContainer *container, bool forced) { const char *name = "Part_NoElementMap"; diff --git a/src/Mod/Part/App/PartFeature.h b/src/Mod/Part/App/PartFeature.h index 0c01a1fe47..33244265e8 100644 --- a/src/Mod/Part/App/PartFeature.h +++ b/src/Mod/Part/App/PartFeature.h @@ -63,6 +63,7 @@ public: virtual ~Feature(); PropertyPartShape Shape; + App::PropertyLinkSubHidden ColoredElements; /** @name methods override feature */ //@{ @@ -154,6 +155,12 @@ public: virtual const std::vector& getElementTypes(bool all=false) const override; + boost::signals2::signal signalMapShapeColors; + + static Feature *create(const TopoShape &s, + const char *name = nullptr, + App::Document *doc = nullptr); + protected: /// recompute only this object virtual App::DocumentObjectExecReturn *recompute() override; diff --git a/src/Mod/Part/Gui/CommandSimple.cpp b/src/Mod/Part/Gui/CommandSimple.cpp index 87263d5a99..f6d798ae41 100644 --- a/src/Mod/Part/Gui/CommandSimple.cpp +++ b/src/Mod/Part/Gui/CommandSimple.cpp @@ -30,7 +30,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -215,18 +216,30 @@ static void _copyShape(const char *cmdName, bool resolve,bool needElement=false, for(auto &v : subMap) { Gui::Command::doCommand(Gui::Command::Doc, "__shape = Part.getShape(%s,'%s',needSubElement=%s,refine=%s)%s\n" - "App.ActiveDocument.addObject('Part::Feature','%s').Shape=__shape\n" + "App.ActiveDocument.addObject('Part::Feature','ShapeCopy').Shape=__shape\n" "App.ActiveDocument.ActiveObject.Label=%s.Label\n", - parentName.c_str(), v.first.c_str(), + parentName.c_str(), + v.first.c_str(), needElement ? "True" : "False", refine ? "True" : "False", needElement ? ".copy()" : "", - v.second->getNameInDocument(), Gui::Command::getObjectCmd(v.second).c_str()); auto newObj = App::GetApplication().getActiveDocument()->getActiveObject(); Gui::Command::copyVisual(newObj, "ShapeColor", v.second); Gui::Command::copyVisual(newObj, "LineColor", v.second); Gui::Command::copyVisual(newObj, "PointColor", v.second); + Gui::cmdGuiObject(newObj,std::ostringstream() + << "mapShapeColors(App.getDocument('" << obj->getDocument()->getName() << "'))"); + if(obj->getDocument() != App::GetApplication().getActiveDocument()) { + // clear element map if copied from external object, or else + // weird stuff will happen + Gui::Command::doCommand(Gui::Command::Doc, + "__shape.ElementMap = {}\n" + "__shape.Tag = 0\n" + "%s.Shape = __shape\n", + Gui::Command::getObjectCmd(newObj).c_str()); + } + Gui::Command::doCommand(Gui::Command::Doc,"del(__shape)"); } } Gui::Command::commitCommand(); @@ -329,14 +342,13 @@ void CmdPartRefineShape::activated(int iMsg) openCommand(QT_TRANSLATE_NOOP("Command", "Refine shape")); std::for_each(objs.begin(), objs.end(), [](App::DocumentObject* obj) { try { - doCommand(Doc,"App.ActiveDocument.addObject('Part::Refine','%s').Source=" + doCommand(Doc,"App.ActiveDocument.addObject('Part::Refine','Refined').Source=" "App.ActiveDocument.%s\n" "App.ActiveDocument.ActiveObject.Label=" "App.ActiveDocument.%s.Label\n" "Gui.ActiveDocument.%s.hide()\n", obj->getNameInDocument(), obj->getNameInDocument(), - obj->getNameInDocument(), obj->getNameInDocument()); copyVisual("ActiveObject", "ShapeColor", obj->getNameInDocument()); diff --git a/src/Mod/Part/Gui/ViewProviderExt.cpp b/src/Mod/Part/Gui/ViewProviderExt.cpp index c0431075d8..f83313bbf6 100644 --- a/src/Mod/Part/Gui/ViewProviderExt.cpp +++ b/src/Mod/Part/Gui/ViewProviderExt.cpp @@ -27,6 +27,7 @@ # include # include # include +# include # include # include # include @@ -66,38 +67,71 @@ # include # include # include +# include #endif +#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 #include "ViewProviderExt.h" +#include "PartParams.h" #include "SoBrepEdgeSet.h" #include "SoBrepFaceSet.h" #include "SoBrepPointSet.h" -#include "TaskFaceColors.h" - +#include "ViewProviderPartExtPy.h" FC_LOG_LEVEL_INIT("Part", true, true) using namespace PartGui; +namespace bio = boost::iostreams; PROPERTY_SOURCE(PartGui::ViewProviderPartExt, Gui::ViewProviderGeometryObject) +namespace PartGui { + +// Private class used by ViewProviderExt to update its visual nodes up on +// receiving SoGetBoundingBoxAction +class SoFCCoordinate3: public SoCoordinate3 +{ +public: + virtual void getBoundingBox(SoGetBoundingBoxAction * action) { + if (vp && vp->VisualTouched) + vp->updateVisual(); + SoCoordinate3::getBoundingBox(action); + } + + ViewProviderPartExt *vp = nullptr; +}; + +} // namespace PartGui + //************************************************************************** // Construction/Destruction @@ -109,6 +143,7 @@ const char* ViewProviderPartExt::DrawStyleEnums[]= {"Solid","Dashed","Dotted","D ViewProviderPartExt::ViewProviderPartExt() { + UpdatingColor = false; VisualTouched = true; forceUpdateCount = 0; NormalsFromUV = true; @@ -167,12 +202,12 @@ ViewProviderPartExt::ViewProviderPartExt() LineWidth.setConstraints(&sizeRange); PointSize.setConstraints(&sizeRange); ADD_PROPERTY_TYPE(PointSize,(psize), osgroup, App::Prop_None, "Set object point size."); - ADD_PROPERTY_TYPE(Deviation,(0.5f), osgroup, App::Prop_None, + ADD_PROPERTY_TYPE(Deviation,(PartParams::getMeshDeviation()), osgroup, App::Prop_None, "Sets the accuracy of the polygonal representation of the model\n" "in the 3D view (tessellation). Lower values indicate better quality.\n" "The value is in percent of object's size."); Deviation.setConstraints(&tessRange); - ADD_PROPERTY_TYPE(AngularDeflection,(28.65), osgroup, App::Prop_None, + ADD_PROPERTY_TYPE(AngularDeflection,(PartParams::getMeshAngularDeflection()), osgroup, App::Prop_None, "Specify how finely to generate the mesh for rendering on screen or when exporting.\n" "The default value is 28.5 degrees, or 0.5 radians. The smaller the value\n" "the smoother the appearance in the 3D view, and the finer the mesh that will be exported."); @@ -182,8 +217,20 @@ ViewProviderPartExt::ViewProviderPartExt() ADD_PROPERTY_TYPE(DrawStyle,((long int)0), osgroup, App::Prop_None, "Defines the style of the edges in the 3D view."); DrawStyle.setEnums(DrawStyleEnums); - coords = new SoCoordinate3(); + ADD_PROPERTY_TYPE(MappedColors,(),"", + (App::PropertyType)(App::Prop_Hidden|App::Prop_ReadOnly),""); + + ADD_PROPERTY(MapFaceColor,(PartParams::getMapFaceColor())); + ADD_PROPERTY(MapLineColor,(PartParams::getMapLineColor())); + ADD_PROPERTY(MapPointColor,(PartParams::getMapPointColor())); + ADD_PROPERTY(MapTransparency,(PartParams::getMapTransparency())); + ADD_PROPERTY(ForceMapColors,(false)); + + coords = new SoFCCoordinate3(); + static_cast(coords)->vp = this; coords->ref(); + pcoords = new SoCoordinate3(); + pcoords->ref(); faceset = new SoBrepFaceSet(); faceset->ref(); norm = new SoNormal; @@ -228,7 +275,6 @@ ViewProviderPartExt::ViewProviderPartExt() DrawStyle.touch(); sPixmap = "Part_3D_object"; - loadParameter(); } ViewProviderPartExt::~ViewProviderPartExt() @@ -241,7 +287,9 @@ ViewProviderPartExt::~ViewProviderPartExt() pcLineStyle->unref(); pcPointStyle->unref(); pShapeHints->unref(); + static_cast(coords)->vp = nullptr; coords->unref(); + pcoords->unref(); faceset->unref(); norm->unref(); normb->unref(); @@ -251,20 +299,73 @@ ViewProviderPartExt::~ViewProviderPartExt() void ViewProviderPartExt::onChanged(const App::Property* prop) { + Gui::ColorUpdater colorUpdater; + + if (prop == &MappedColors || + prop == &MapFaceColor || + prop == &MapLineColor || + prop == &MapPointColor || + prop == &MapTransparency || + prop == &ForceMapColors) + { + if(!prop->testStatus(App::Property::User3)) { + if(prop == &MapFaceColor) { + if(!MapFaceColor.getValue()) { + ShapeColor.touch(); + return; + } + }else if(prop == &MapLineColor) { + if(!MapLineColor.getValue()) { + LineColor.touch(); + return; + } + }else if(prop == &MapPointColor) { + if(!MapPointColor.getValue()) { + PointColor.touch(); + return; + } + } + updateColors(); + } + return; + } + + if (isRestoring()) { + if (prop == &LineColor + || prop == &LineMaterial + || prop == &PointColor + || prop == &PointMaterial + || prop == &ShapeColor + || prop == &ShapeMaterial) + { + // When restoring, rely on + // DiffuseColor/LineColorArray/PointColorArray to setup the colors. + // Because the order of restoring say DiffuseColor and ShapeColor + // may be different depending on whether the PropertyColorList is + // saved into a separate file or embedded inside xml. + ViewProviderDocumentObject::onChanged(prop); + return; + } + } + // The lower limit of the deviation has been increased to avoid // to freeze the GUI // https://forum.freecadweb.org/viewtopic.php?f=3&t=24912&p=195613 if (prop == &Deviation) { - if(isUpdateForced()||Visibility.getValue()) - updateVisual(); - else - VisualTouched = true; + if (!prop->testStatus(App::Property::User3)) { + if(isUpdateForced()||Visibility.getValue()) + updateVisual(); + else + VisualTouched = true; + } } if (prop == &AngularDeflection) { - if(isUpdateForced()||Visibility.getValue()) - updateVisual(); - else - VisualTouched = true; + if (!prop->testStatus(App::Property::User3)) { + if(isUpdateForced()||Visibility.getValue()) + updateVisual(); + else + VisualTouched = true; + } } if (prop == &LineWidth) { pcLineStyle->lineWidth = LineWidth.getValue(); @@ -278,6 +379,8 @@ void ViewProviderPartExt::onChanged(const App::Property* prop) if (c != LineMaterial.getValue().diffuseColor) LineMaterial.setDiffuseColor(c); LineColorArray.setValue(LineColor.getValue()); + if(!prop->testStatus(App::Property::User3)) + updateColors(); } else if (prop == &PointColor) { const App::Color& c = PointColor.getValue(); @@ -285,6 +388,8 @@ void ViewProviderPartExt::onChanged(const App::Property* prop) if (c != PointMaterial.getValue().diffuseColor) PointMaterial.setDiffuseColor(c); PointColorArray.setValue(PointColor.getValue()); + if(!prop->testStatus(App::Property::User3)) + updateColors(); } else if (prop == &LineMaterial) { const App::Material& Mat = LineMaterial.getValue(); @@ -310,34 +415,49 @@ void ViewProviderPartExt::onChanged(const App::Property* prop) } else if (prop == &PointColorArray) { setHighlightedPoints(PointColorArray.getValues()); + Gui::ColorUpdater::addObject(getObject()); } else if (prop == &LineColorArray) { setHighlightedEdges(LineColorArray.getValues()); + Gui::ColorUpdater::addObject(getObject()); } else if (prop == &DiffuseColor) { setHighlightedFaces(DiffuseColor.getValues()); + Gui::ColorUpdater::addObject(getObject()); } - else if (prop == &ShapeMaterial || prop == &ShapeColor) { - pcFaceBind->value = SoMaterialBinding::OVERALL; - ViewProviderGeometryObject::onChanged(prop); - App::Color c = ShapeColor.getValue(); - c.a = Transparency.getValue()/100.0f; - DiffuseColor.setValue(c); + else if(prop == &ShapeColor) { + if(!ShapeColor.testStatus(App::Property::User3)) { + Base::ObjectStatusLocker guard( + App::Property::User3, &ShapeColor); + ViewProviderGeometryObject::onChanged(prop); + App::Color c = ShapeColor.getValue(); + c.a = Transparency.getValue()/100.0f; + DiffuseColor.setValue(c); + updateColors(); + } + return; } else if (prop == &Transparency) { const App::Material& Mat = ShapeMaterial.getValue(); long value = (long)(100*Mat.transparency); if (value != Transparency.getValue()) { float trans = Transparency.getValue()/100.0f; - auto colors = DiffuseColor.getValues(); - for (auto &c : colors) - c.a = trans; - DiffuseColor.setValues(colors); App::PropertyContainer* parent = ShapeMaterial.getContainer(); ShapeMaterial.setContainer(nullptr); ShapeMaterial.setTransparency(trans); ShapeMaterial.setContainer(parent); + + if(!prop->testStatus(App::Property::User3)) { + if(MapTransparency.getValue() || MappedColors.getSize()) { + updateColors(); + } else{ + auto colors = DiffuseColor.getValues(); + for (auto &c : colors) + c.a = trans; + DiffuseColor.setValues(colors); + } + } } } else if (prop == &Lighting) { @@ -360,14 +480,6 @@ void ViewProviderPartExt::onChanged(const App::Property* prop) // if the object was invisible and has been changed, recreate the visual if (prop == &Visibility && (isUpdateForced() || Visibility.getValue()) && VisualTouched) { updateVisual(); - // updateVisual() may not be triggered by any change (e.g. - // triggered by an external object through forceUpdate()). And - // since DiffuseColor is not changed here either, do not falsely set - // the document modified status - Base::ObjectStatusLocker guard( - App::Property::NoModify, &DiffuseColor); - // The material has to be checked again (#0001736) - onChanged(&DiffuseColor); } } @@ -385,6 +497,12 @@ void ViewProviderPartExt::attach(App::DocumentObject *pcFeat) // call parent attach method ViewProviderGeometryObject::attach(pcFeat); + if (auto feat = Base::freecad_dynamic_cast(pcFeat)) { + conn = feat->signalMapShapeColors.connect([this](App::Document *doc) { + updateColors(doc, true); + }); + } + // Workaround for #0000433, i.e. use SoSeparator instead of SoGroup auto* pcNormalRoot = new SoSeparator(); auto* pcFlatRoot = new SoSeparator(); @@ -418,10 +536,10 @@ void ViewProviderPartExt::attach(App::DocumentObject *pcFeat) wireframe->addChild(lineset); // normal viewing with edges and points - pcNormalRoot->addChild(pcPointsRoot); - pcNormalRoot->addChild(wireframe); pcNormalRoot->addChild(offset); pcNormalRoot->addChild(pcFlatRoot); + pcNormalRoot->addChild(wireframe); + pcNormalRoot->addChild(pcPointsRoot); // just faces with no edges or points pcFlatRoot->addChild(pShapeHints); @@ -439,6 +557,10 @@ void ViewProviderPartExt::attach(App::DocumentObject *pcFeat) pcWireframeRoot->addChild(pcPointsRoot); // normal viewing with edges and points + auto pnormb = new SoNormalBinding; + pnormb->value = SoNormalBinding::OVERALL; + pcPointsRoot->addChild(pnormb); + pcPointsRoot->addChild(pcoords); pcPointsRoot->addChild(pcPointBind); pcPointsRoot->addChild(pcPointMaterial); pcPointsRoot->addChild(pcPointStyle); @@ -482,54 +604,131 @@ std::vector ViewProviderPartExt::getDisplayModes(void) const return StrList; } -std::string ViewProviderPartExt::getElement(const SoDetail* detail) const +Part::TopoShape ViewProviderPartExt::getShape() const { - std::stringstream str; - if (detail) { - if (detail->getTypeId() == SoFaceDetail::getClassTypeId()) { - const SoFaceDetail* face_detail = static_cast(detail); - int face = face_detail->getPartIndex() + 1; - str << "Face" << face; - } - else if (detail->getTypeId() == SoLineDetail::getClassTypeId()) { - const SoLineDetail* line_detail = static_cast(detail); - int edge = line_detail->getLineIndex() + 1; - str << "Edge" << edge; - } - else if (detail->getTypeId() == SoPointDetail::getClassTypeId()) { - const SoPointDetail* point_detail = static_cast(detail); - int vertex = point_detail->getCoordinateIndex() - nodeset->startIndex.getValue() + 1; - str << "Vertex" << vertex; - } - } + Part::TopoShape shape; + if (!isAttachedToDocument() || !getObject()) + return shape; - return str.str(); + auto prop = Base::freecad_dynamic_cast( + getObject()->getPropertyByName(getShapePropertyName())); + if (prop) + return prop->getShape(); + return Part::Feature::getTopoShape(getObject()); +} + +void ViewProviderPartExt::setShapePropertyName(const char *propName) +{ + if(propName) + shapePropName = propName; + else + shapePropName.clear(); + if (isUpdateForced()||Visibility.getValue()) + updateVisual(); + else + VisualTouched = true; +} + +const char * ViewProviderPartExt::getShapePropertyName() const +{ + return shapePropName.empty()?"Shape":shapePropName.c_str(); +} + +bool ViewProviderPartExt::getElementPicked(const SoPickedPoint *pp, std::string &subname) const +{ + const SoDetail *detail = pp->getDetail(); + if (!detail) + return inherited::getElementPicked(pp,subname); + + std::ostringstream ss; + auto node = pp->getPath()->getTail(); + if (node == faceset && detail->isOfType(SoFaceDetail::getClassTypeId())) { + const SoFaceDetail* face_detail = static_cast(detail); + int face = face_detail->getPartIndex() + 1; + ss << "Face" << face; + } else if (node == lineset && detail->isOfType(SoLineDetail::getClassTypeId())) { + const SoLineDetail* line_detail = static_cast(detail); + int edge = line_detail->getLineIndex() + 1; + ss << "Edge" << edge; + } else if (node == nodeset && detail->isOfType(SoPointDetail::getClassTypeId())) { + const SoPointDetail* point_detail = static_cast(detail); + int vertex = point_detail->getCoordinateIndex() - nodeset->startIndex.getValue() + 1; + ss << "Vertex" << vertex; + } else + return inherited::getElementPicked(pp,subname); + + subname = ss.str(); + return true; +} + +std::string ViewProviderPartExt::getElement(const SoDetail *detail) const +{ + // This function is providered here for backward compatibility. It provides + // mostly the same function as getElementPick(), but cannot work with exotic + // viewproviders that also group other nodes here. + if (!detail) + return inherited::getElement(detail); + + std::ostringstream ss; + if (detail->isOfType(SoFaceDetail::getClassTypeId())) { + const SoFaceDetail* face_detail = static_cast(detail); + int face = face_detail->getPartIndex() + 1; + ss << "Face" << face; + } else if (detail->isOfType(SoLineDetail::getClassTypeId())) { + const SoLineDetail* line_detail = static_cast(detail); + int edge = line_detail->getLineIndex() + 1; + ss << "Edge" << edge; + } else if (detail->isOfType(SoPointDetail::getClassTypeId())) { + const SoPointDetail* point_detail = static_cast(detail); + int vertex = point_detail->getCoordinateIndex() - nodeset->startIndex.getValue() + 1; + ss << "Vertex" << vertex; + } else + return inherited::getElement(detail); + + return ss.str(); +} + +bool ViewProviderPartExt::getDetailPath(const char *subname, + SoFullPath *pPath, + bool append, + SoDetail *&det) const +{ + auto element = Data::ComplexGeoData::findElementName(subname); + if (element && element == subname) { + if (Data::ComplexGeoData::hasMissingElement(subname)) + return false; + } + return inherited::getDetailPath(subname, pPath, append, det); } SoDetail* ViewProviderPartExt::getDetail(const char* subelement) const { - std::string element; - int index; - SoDetail* detail = nullptr; - boost::regex ex("^(Face|Edge|Vertex)([1-9][0-9]*)$"); - boost::cmatch what; + const auto &shape = getShape(); + Data::IndexedName element = shape.getElementName(subelement).index; + auto res = shape.shapeTypeAndIndex(element); + if(!res.second) + return nullptr; - if (boost::regex_match(subelement, what, ex)) { - element = what[1].str(); - index = std::atoi(what[2].str().c_str()); + Part::TopoShape subshape = shape.getSubTopoShape(res.first, res.second, true); + if(subshape.isNull()) + return nullptr; - if (element == "Face") { - detail = new SoFaceDetail(); - static_cast(detail)->setPartIndex(index - 1); - } - else if (element == "Edge") { - detail = new SoLineDetail(); - static_cast(detail)->setLineIndex(index - 1); - } - else if (element == "Vertex") { - detail = new SoPointDetail(); - static_cast(detail)->setCoordinateIndex(index + nodeset->startIndex.getValue() - 1); - } + SoDetail *detail = nullptr; + switch(res.first) { + case TopAbs_FACE: + detail = new SoFaceDetail(); + static_cast(detail)->setPartIndex(res.second - 1); + break; + case TopAbs_EDGE: + detail = new SoLineDetail(); + static_cast(detail)->setLineIndex(res.second - 1); + break; + case TopAbs_VERTEX: + detail = new SoPointDetail(); + static_cast(detail)->setCoordinateIndex(res.second + nodeset->startIndex.getValue() - 1); + break; + default: + break; } return detail; @@ -539,8 +738,9 @@ std::vector ViewProviderPartExt::getModelPoints(const SoPickedPo { try { std::vector pts; - std::string element = this->getElement(pp->getDetail()); - const auto &shape = Part::Feature::getTopoShape(getObject()); + std::string element; + this->getElementPicked(pp, element); + const auto &shape = getShape(); TopoDS_Shape subShape = shape.getSubShape(element.c_str()); @@ -587,68 +787,98 @@ std::vector ViewProviderPartExt::getSelectionShape(const char* / void ViewProviderPartExt::setHighlightedFaces(const std::vector& colors) { - if (getObject() && getObject()->testStatus(App::ObjectStatus::TouchOnColorChange)) - getObject()->touch(true); - Gui::SoUpdateVBOAction action; action.apply(this->faceset); int size = static_cast(colors.size()); - if (size > 1 && size == this->faceset->partIndex.getNum()) { + if (size > 1) { + int numfaces = this->faceset->partIndex.getNum(); + if(size > numfaces) + size = numfaces; pcFaceBind->value = SoMaterialBinding::PER_PART; - pcShapeMaterial->diffuseColor.setNum(size); - pcShapeMaterial->transparency.setNum(size); + pcShapeMaterial->diffuseColor.setNum(numfaces); + pcShapeMaterial->transparency.setNum(numfaces); SbColor* ca = pcShapeMaterial->diffuseColor.startEditing(); float *t = pcShapeMaterial->transparency.startEditing(); - for (int i = 0; i < size; i++) { + int i=0; + for (; i < size; i++) { ca[i].setValue(colors[i].r, colors[i].g, colors[i].b); t[i] = colors[i].a; } + const auto &color = ShapeColor.getValue(); + float trans = ShapeMaterial.getValue().transparency; + for (; i < numfaces; i++) { + ca[i].setValue(color.r, color.g, color.b); + t[i] = trans; + } pcShapeMaterial->diffuseColor.finishEditing(); pcShapeMaterial->transparency.finishEditing(); + return; } - else if (colors.size() == 1) { - pcFaceBind->value = SoMaterialBinding::OVERALL; - pcShapeMaterial->diffuseColor.setValue(colors[0].r, colors[0].g, colors[0].b); - pcShapeMaterial->transparency = Transparency.getValue()/100.f; - } + + const auto &color = colors.size()==1?colors[0]:ShapeColor.getValue(); + pcFaceBind->value = SoMaterialBinding::OVERALL; + pcShapeMaterial->diffuseColor.setValue(color.r, color.g, color.b); + //pcShapeMaterial->transparency = colors[0].a; do not get transparency from DiffuseColor in this case + pcShapeMaterial->transparency.setValue(ShapeMaterial.getValue().transparency); } void ViewProviderPartExt::setHighlightedFaces(const std::vector& colors) { int size = static_cast(colors.size()); - if (size > 1 && size == this->faceset->partIndex.getNum()) { + if (size > 1) { + int numfaces = this->faceset->partIndex.getNum(); + if(size > numfaces) + size = numfaces; + pcFaceBind->value = SoMaterialBinding::PER_PART; - pcShapeMaterial->diffuseColor.setNum(size); - pcShapeMaterial->ambientColor.setNum(size); - pcShapeMaterial->specularColor.setNum(size); - pcShapeMaterial->emissiveColor.setNum(size); + pcShapeMaterial->diffuseColor.setNum(numfaces); + pcShapeMaterial->ambientColor.setNum(numfaces); + pcShapeMaterial->specularColor.setNum(numfaces); + pcShapeMaterial->emissiveColor.setNum(numfaces); SbColor* dc = pcShapeMaterial->diffuseColor.startEditing(); SbColor* ac = pcShapeMaterial->ambientColor.startEditing(); SbColor* sc = pcShapeMaterial->specularColor.startEditing(); SbColor* ec = pcShapeMaterial->emissiveColor.startEditing(); - for (int i = 0; i < size; i++) { + int i=0; + for (; i < size; i++) { dc[i].setValue(colors[i].diffuseColor.r, colors[i].diffuseColor.g, colors[i].diffuseColor.b); ac[i].setValue(colors[i].ambientColor.r, colors[i].ambientColor.g, colors[i].ambientColor.b); sc[i].setValue(colors[i].specularColor.r, colors[i].specularColor.g, colors[i].specularColor.b); ec[i].setValue(colors[i].emissiveColor.r, colors[i].emissiveColor.g, colors[i].emissiveColor.b); } + const auto &material = ShapeMaterial.getValue(); + for (; i < numfaces; ++i) { + dc[i].setValue(material.diffuseColor.r, material.diffuseColor.g, material.diffuseColor.b); + ac[i].setValue(material.ambientColor.r, material.ambientColor.g, material.ambientColor.b); + sc[i].setValue(material.specularColor.r, material.specularColor.g, material.specularColor.b); + ec[i].setValue(material.emissiveColor.r, material.emissiveColor.g, material.emissiveColor.b); + } + pcShapeMaterial->diffuseColor.finishEditing(); pcShapeMaterial->ambientColor.finishEditing(); pcShapeMaterial->specularColor.finishEditing(); pcShapeMaterial->emissiveColor.finishEditing(); + return; } - else if (colors.size() == 1) { - pcFaceBind->value = SoMaterialBinding::OVERALL; - pcShapeMaterial->diffuseColor.setValue(colors[0].diffuseColor.r, colors[0].diffuseColor.g, colors[0].diffuseColor.b); - pcShapeMaterial->ambientColor.setValue(colors[0].ambientColor.r, colors[0].ambientColor.g, colors[0].ambientColor.b); - pcShapeMaterial->specularColor.setValue(colors[0].specularColor.r, colors[0].specularColor.g, colors[0].specularColor.b); - pcShapeMaterial->emissiveColor.setValue(colors[0].emissiveColor.r, colors[0].emissiveColor.g, colors[0].emissiveColor.b); - } + + const auto &material = colors.size()==1?colors[0]:ShapeMaterial.getValue(); + pcFaceBind->value = SoMaterialBinding::OVERALL; + pcShapeMaterial->diffuseColor.setValue(material.diffuseColor.r, material.diffuseColor.g, material.diffuseColor.b); + pcShapeMaterial->ambientColor.setValue(material.ambientColor.r, material.ambientColor.g, material.ambientColor.b); + pcShapeMaterial->specularColor.setValue(material.specularColor.r, material.specularColor.g, material.specularColor.b); + pcShapeMaterial->emissiveColor.setValue(material.emissiveColor.r, material.emissiveColor.g, material.emissiveColor.b); +} + +static inline App::PropertyLinkSub *getColoredElements(const App::DocumentObject *obj) { + if(!obj || !obj->getNameInDocument()) + return 0; + return Base::freecad_dynamic_cast( + obj->getPropertyByName("ColoredElements")); } std::map ViewProviderPartExt::getElementColors(const char *element) const { @@ -660,9 +890,34 @@ std::map ViewProviderPartExt::getElementColors(const cha ret["Face"] = color; ret["Edge"] = LineColor.getValue(); ret["Vertex"] = PointColor.getValue(); + + auto prop = getColoredElements(pcObject); + if(prop && prop->getValue()==pcObject) { + const auto &subs = prop->getSubValues(); + const auto &colors = MappedColors.getValues(); + if(subs.size()==colors.size()) { + for(size_t i=0;i ViewProviderPartExt::getElementColors(const cha return ret; } +void ViewProviderPartExt::setElementColors(const std::map &info) +{ + auto propColoredElements = getColoredElements(pcObject); + if(!propColoredElements) + return; + std::vector colors; + std::vector subs; + colors.reserve(info.size()); + subs.reserve(info.size()); + bool touched = false; + for(auto &v : info) { + if(v.first == "Face") { + if(ShapeColor.getValue()!=v.second) { + touched = true; + ShapeColor.setValue(v.second); + } + if(v.second.a*100 != Transparency.getValue()) { + Transparency.setValue(v.second.a*100); + touched = true; + } + } else if(v.first == "Edge") { + if(LineColor.getValue()!=v.second) { + LineColor.setValue(v.second); + touched = true; + } + } else if(v.first == "Vertex"){ + if(PointColor.getValue()!=v.second) { + PointColor.setValue(v.second); + touched = true; + } + } else { + subs.push_back(v.first); + colors.push_back(v.second); + } + } + if(colors!=MappedColors.getValues()) { + touched = true; + Base::ObjectStatusLocker guard( + App::Property::User3, &MappedColors); + MappedColors.setValues(colors); + } + if(subs.empty()) + propColoredElements->setValue(0); + else if(subs!=propColoredElements->getSubValues()) + propColoredElements->setValue(pcObject,subs); + else if(touched) + updateColors(); +} + void ViewProviderPartExt::unsetHighlightedFaces() { - DiffuseColor.touch(); - Transparency.touch(); + setHighlightedFaces(DiffuseColor.getValues()); } void ViewProviderPartExt::setHighlightedEdges(const std::vector& colors) { - if (getObject() && getObject()->testStatus(App::ObjectStatus::TouchOnColorChange)) - getObject()->touch(true); int size = static_cast(colors.size()); if (size > 1) { // Although indexed lineset is used the material binding must be PER_FACE! pcLineBind->value = SoMaterialBinding::PER_FACE; const int32_t* cindices = this->lineset->coordIndex.getValues(0); int numindices = this->lineset->coordIndex.getNum(); - pcLineMaterial->diffuseColor.setNum(size); - SbColor* ca = pcLineMaterial->diffuseColor.startEditing(); int linecount = 0; - for (int i = 0; i < numindices; ++i) { - if (cindices[i] < 0) { - ca[linecount].setValue(colors[linecount].r, colors[linecount].g, colors[linecount].b); - linecount++; - if (linecount >= size) - break; - } + if (cindices[i] < 0) + ++linecount; } + pcLineMaterial->diffuseColor.setNum(linecount); + SbColor* ca = pcLineMaterial->diffuseColor.startEditing(); + + if(size > linecount) + size = linecount; + + int i=0; + for (; i < size; ++i) + ca[i].setValue(colors[i].r, colors[i].g, colors[i].b); + + const auto &color = LineColor.getValue(); + for (; i < linecount; ++i) + ca[i].setValue(color.r, color.g, color.b); pcLineMaterial->diffuseColor.finishEditing(); + return; } - else if (size == 1) { - pcLineBind->value = SoMaterialBinding::OVERALL; - pcLineMaterial->diffuseColor.setValue(colors[0].r, colors[0].g, colors[0].b); - } + + const auto &color = colors.size()==1?colors[0]:LineColor.getValue(); + pcLineBind->value = SoMaterialBinding::OVERALL; + pcLineMaterial->diffuseColor.setValue(color.r, color.g, color.b); } void ViewProviderPartExt::unsetHighlightedEdges() { - pcLineBind->value = SoMaterialBinding::OVERALL; - LineMaterial.touch(); + setHighlightedEdges(LineColorArray.getValues()); } void ViewProviderPartExt::setHighlightedPoints(const std::vector& colors) { - if (getObject() && getObject()->testStatus(App::ObjectStatus::TouchOnColorChange)) - getObject()->touch(true); int size = static_cast(colors.size()); if (size > 1) { -#if 0 - int numPoints = coords->point.getNum() - nodeset->startIndex.getValue(); - if (numPoints != size) { - SoDebugError::postWarning("ViewProviderPartExt::setHighlightedPoints", - "The number of points (%d) doesn't match with the number of colors (%d).", numPoints, size); - } -#endif + int numpoints = pcoords->point.getNum(); + if(size > numpoints) + size = numpoints; pcPointBind->value = SoMaterialBinding::PER_VERTEX; - pcPointMaterial->diffuseColor.setNum(size); + pcPointMaterial->diffuseColor.setNum(numpoints); SbColor* ca = pcPointMaterial->diffuseColor.startEditing(); - for (int i = 0; i < size; ++i) + int i=0; + for (; i < size; ++i) ca[i].setValue(colors[i].r, colors[i].g, colors[i].b); + const auto &color = PointColor.getValue(); + for (; i < numpoints; ++i) + ca[i].setValue(color.r, color.g, color.b); pcPointMaterial->diffuseColor.finishEditing(); } - else if (size == 1) { - pcPointBind->value = SoMaterialBinding::OVERALL; - pcPointMaterial->diffuseColor.setValue(colors[0].r, colors[0].g, colors[0].b); - } + + const auto &color = size==1?colors[0]:PointColor.getValue(); + pcPointBind->value = SoMaterialBinding::OVERALL; + pcPointMaterial->diffuseColor.setValue(color.r, color.g, color.b); } void ViewProviderPartExt::unsetHighlightedPoints() { - PointColorArray.touch(); -} - -bool ViewProviderPartExt::loadParameter() -{ - bool changed = false; - ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath - ("User parameter:BaseApp/Preferences/Mod/Part"); - float deviation = hGrp->GetFloat("MeshDeviation",0.2); - float angularDeflection = hGrp->GetFloat("MeshAngularDeflection",28.65); - NormalsFromUV = hGrp->GetBool("NormalsFromUVNodes", NormalsFromUV); - - if (Deviation.getValue() != deviation) { - Deviation.setValue(deviation); - changed = true; - } - if (AngularDeflection.getValue() != angularDeflection ) { - AngularDeflection.setValue(angularDeflection); - } - - return changed; + setHighlightedPoints(PointColorArray.getValues()); } void ViewProviderPartExt::reload() { - if (loadParameter()) - updateVisual(); + bool update = false; + double pointsize = PointSize.getValue(); + double linewidth = LineWidth.getValue(); + if (PartParams::getRespectSystemDPI()) { + auto dpi = qApp->devicePixelRatio(); + pointsize = std::max(1.0, pointsize*dpi); + linewidth = std::max(1.0, linewidth*dpi); + } + if (pcPointStyle->pointSize.getValue() != pointsize + || pcLineStyle->lineWidth.getValue() != linewidth) + { + pcPointStyle->pointSize = pointsize; + pcLineStyle->lineWidth = linewidth; + update = true; + } + if (NormalsFromUV != PartParams::getNormalsFromUVNodes()) { + update = true; + NormalsFromUV = !NormalsFromUV; + } + + tessRange.LowerBound = PartParams::getMinimumDeviation(); + angDeflectionRange.LowerBound = PartParams::getMinimumAngularDeflection(); + + if (Deviation.getValue() != PartParams::getMeshDeviation() + || Deviation.getValue() < PartParams::getMinimumDeviation() + || AngularDeflection.getValue() != PartParams::getMeshAngularDeflection() + || AngularDeflection.getValue() < PartParams::getMinimumAngularDeflection()) + update = true; + + if (!update) + return; + + if (!PartParams::getOverrideTessellation()) { + Base::ObjectStatusLocker guard( + App::Property::User3, &Deviation); + + Deviation.setValue(PartParams::getMeshDeviation()); + Base::ObjectStatusLocker guard2( + App::Property::User3, &AngularDeflection); + AngularDeflection.setValue(PartParams::getMeshAngularDeflection()); + } + updateVisual(); +} + +static bool getLinkColor(const Data::MappedName &mapped, App::DocumentObject *&obj, + ViewProviderPartExt *&svp, App::Color &color) +{ + if(!obj) + return false; + bool colorFound = false; + for(int depth=0;;++depth) { + auto vp = Base::freecad_dynamic_cast( + Gui::Application::Instance->getViewProvider(obj)); + if(vp && vp->getDefaultMode()==1) { + svp = Base::freecad_dynamic_cast( + vp->ChildViewProvider.getObject().get()); + if(svp) + return false; + } + auto link = obj->getExtensionByType(true); + if(vp && vp->OverrideMaterial.getValue()) { + colorFound = true; + color = vp->ShapeMaterial.getValue().diffuseColor; + color.a = vp->ShapeMaterial.getValue().transparency; + if(!link || !link->getElementCountValue()) + return true; + } + // check for link array + if(link && !link->getShowElementValue() && link->getElementCountValue()) { + int pos = mapped.rfind(Part::TopoShape::indexPostfix()); + if(pos >= 0) { + int offset = pos + static_cast(Part::TopoShape::indexPostfix().size()); + QByteArray bytes = mapped.toRawBytes(offset); + bio::stream iss(bytes.constData(), bytes.size()); + int index = 0; + char sep = 0; + iss >> index >> sep; + if(sep==';' && + vp->OverrideMaterialList.getSize()>index && + vp->OverrideMaterialList[index] && + vp->MaterialList.getSize()>index) + { + color = vp->MaterialList[index].diffuseColor; + color.a = vp->MaterialList[index].transparency; + return true; + } + if(colorFound) + return colorFound; + obj = obj->getSubObject((std::to_string(index)+".").c_str()); + return false; + } + } + auto linked = obj->getLinkedObject(false,0,false,depth); + if(!linked || linked==obj) + break; + obj = linked; + } + return colorFound; +} + +struct ElementCache +{ + Part::TopoShape shape; + ViewProviderPartExt *vp; + bool inited = false; +}; + +static App::Color getElementColor(App::Color color, + const Part::TopoShape &shape, + App::Document *doc, + int type, + const Data::MappedName &name, + std::map &caches) +{ + if (!name) + return color; + + Data::MappedName mapped(name); + bool colorFound = false; + std::vector history; + Data::MappedName original; + long tag = shape.getElementHistory(mapped,&original,&history); + while(1) { + if(!tag) + return color; + auto obj = doc->getObjectByID(std::abs(tag)); + if(!obj || !obj->getNameInDocument()) + return color; + auto & cache = caches[obj]; + if (!cache.inited) { + cache.inited = true; + cache.shape = Part::Feature::getTopoShape(obj); + cache.vp = Base::freecad_dynamic_cast( + Gui::Application::Instance->getViewProvider(obj)); + } + const Part::TopoShape & shape = cache.shape; + ViewProviderPartExt *vp = cache.vp; + if(shape.isNull() || getLinkColor(original,obj,vp,color) || !obj) + return color; + if(!vp) { + // Not a part view provider. No problem, just trace deeper into the + // history until we find one. + doc = obj->getDocument(); + mapped = original; + tag = shape.getElementHistory(mapped,&original,&history); + continue; + } + + if(colorFound) + return color; + + float trans = vp->Transparency.getValue()/100.0; + auto prop = &vp->DiffuseColor; + if(type == TopAbs_VERTEX) + prop = &vp->PointColorArray; + else if(type == TopAbs_EDGE) + prop = &vp->LineColorArray; + if(prop->getSize()==0) + return color; + + history.clear(); + mapped = original; + // Normally, TopoShape::getElementHistory() returns the mapped element + // name of the previous step (in 'original'), and tag is the ID of the + // previous feature. 'mapped' is the mapped element name of the next + // modeling step in shape history. + // + // However, if there are intermediate modeling steps, things get a bit + // tricky. getElementHistory() returns intermediate element names in + // 'history', but the last entry of 'history' actually contains the real + // mapped element name of the 'current' modeling step. That's why we are + // calling getElementHistory() for previous modeling step now, before + // retrieving the element for the current step using getElementName(), + // because we need to check intermediate history names of the previous + // model step. The 'original' returned by getElementHistory() here may + // or may not contain a valid element name for the previous step. We can + // only decide after another loop hits here. + tag = shape.getElementHistory(mapped,&original,&history); + Data::IndexedName element = shape.getIndexedName(history.empty()?mapped:history.back()); + auto idx = Part::TopoShape::shapeTypeAndIndex(element); + if(idx.second>0 && idx.second<=(int)shape.countSubShapes(idx.first)) { + if(idx.first==type) { + if(prop->getSize()==1) { + color = prop->getValues()[0]; + color.a = trans; + } + else if(idx.second<=prop->getSize()) + return prop->getValues()[idx.second-1]; + }else{ + // This means the element is generated from a different type of source element, + // e.g. face generated by an edge. + auto aidx = shape.findAncestor(shape.findShape(idx.first,idx.second),(TopAbs_ShapeEnum)type); + if(aidx>0) { + if(prop->getSize()==1) { + color = prop->getValues()[0]; + color.a = trans; + } + else if(aidx<=prop->getSize()) + return prop->getValues()[aidx-1]; + } + } + } + return color; + } +} + +std::vector ViewProviderPartExt::getShapeColors(const Part::TopoShape &shape, + App::Color &defColor, App::Document *sourceDoc, bool linkOnly) +{ + defColor.setPackedValue(Gui::ViewParams::instance()->getDefaultShapeColor()); + defColor.a = 0; + + if(!sourceDoc) { + sourceDoc = App::GetApplication().getActiveDocument(); + if(!sourceDoc) + return {}; + } + size_t count = shape.countSubShapes(TopAbs_FACE); + if(!count) + return {}; + + Data::MappedName mapped = shape.getMappedName( + Data::IndexedName::fromConst("Face", 1), true); + + ViewProviderPartExt *vp=0; + auto obj = sourceDoc->getObjectByID(shape.Tag); + if(getLinkColor(mapped,obj,vp,defColor)) + return {defColor}; + else if(linkOnly || !obj) + return {}; + + if(!vp) + vp = Base::freecad_dynamic_cast( + Gui::Application::Instance->getViewProvider(obj)); + if(vp) { + defColor = vp->ShapeColor.getValue(); + defColor.a = vp->Transparency.getValue()/100.0f; + return vp->DiffuseColor.getValues(); + } + + std::vector colors(count,defColor); + bool touched = false; + std::map caches; + for(size_t i=0;i<=count;++i) { + mapped = shape.getMappedName(Data::IndexedName::fromConst("Face", i+1)); + if (mapped) { + auto color = getElementColor(defColor,shape,sourceDoc,TopAbs_FACE,mapped,caches); + if(color!=defColor) { + colors[i] = color; + touched = true; + } + } + } + if(!touched) + return {}; + return colors; +} + +bool ViewProviderPartExt::hasBaseFeature() const { + return !claimChildren().empty(); +} + +struct ColorInfo { + TopAbs_ShapeEnum type; + App::PropertyColorList *prop = 0; + App::Color defaultColor; + std::map colors; + bool mapColor; + + void init(TopAbs_ShapeEnum t, ViewProviderPartExt *vp) { + type = t; + switch(type) { + case TopAbs_VERTEX: + defaultColor = vp->PointColor.getValue(); + prop = &vp->PointColorArray; + mapColor = vp->MapPointColor.getValue(); + break; + case TopAbs_EDGE: + defaultColor = vp->LineColor.getValue(); + prop = &vp->LineColorArray; + mapColor = vp->MapLineColor.getValue(); + break; + case TopAbs_FACE: + defaultColor = vp->ShapeColor.getValue(); + defaultColor.a = vp->Transparency.getValue()/100.0f; + prop = &vp->DiffuseColor; + mapColor = vp->MapFaceColor.getValue(); + break; + default: + assert(0); + } + } +}; + +void ViewProviderPartExt::checkColorUpdate() +{ + if (MapFaceColor.getValue() + || MapLineColor.getValue() + || MapPointColor.getValue() + || MapTransparency.getValue()) + updateColors(); +} + +void ViewProviderPartExt::updateColors(App::Document *sourceDoc, bool forceColorMap) +{ + if (UpdatingColor + || !getObject() + || !getObject()->getDocument() + || getObject()->getDocument()->testStatus(App::Document::Restoring)) + return; + + auto geoFeature = Base::freecad_dynamic_cast(pcObject); + + Base::FlagToggler<> flag(UpdatingColor); + auto prop = getColoredElements(pcObject); + if(prop && prop->getSubValues().size()!=(size_t)MappedColors.getSize()) { + if(prop->getSubValues().size()<(size_t)MappedColors.getSize()) + MappedColors.setSize(prop->getSubValues().size()); + else { + auto subs = prop->getSubValues(); + subs.resize(MappedColors.getSize()); + prop->setValue(pcObject,subs); + } + return; + } + + auto shape = getShape(); + if(shape.isNull()) + return; + + if(!sourceDoc) + sourceDoc = pcObject->getDocument(); + + std::vector > _subs; + const auto &subs = prop?prop->getShadowSubs():_subs; + + std::array infos; + infos[TopAbs_VERTEX].init(TopAbs_VERTEX,this); + infos[TopAbs_EDGE].init(TopAbs_EDGE,this); + infos[TopAbs_FACE].init(TopAbs_FACE,this); + bool noColorMap = !ForceMapColors.getValue() && !forceColorMap && !hasBaseFeature(); + + std::set subMap; + for(auto &v : subs) { + if(v.first.size()) + subMap.insert(shape.getElementName(v.first.c_str()).name); + } + int i=-1; + for(auto &v : subs) { + ++i; + Data::IndexedName element; + if (v.first.size()) + element = shape.getElementName(v.first.c_str()).index; + else + element = Data::IndexedName(v.second.c_str()); + auto idx = shape.shapeTypeAndIndex(element); + if(idx.second) { + infos[idx.first].colors[idx.second-1] = MappedColors[i]; + continue; + }else if(v.first.empty()) + continue; + + for(auto &names : Part::Feature::getRelatedElements(pcObject,v.first.c_str())) { + if(!subMap.insert(names.name).second) + continue; + auto idx = Part::TopoShape::shapeTypeAndIndex(names.index); + if(idx.second>0) + infos[idx.first].colors[idx.second-1] = MappedColors[i]; + } + } + std::map caches; + for(auto &info : infos) { + if(!info.prop) continue; + if(noColorMap || !info.mapColor) { + if(info.colors.empty()) + info.prop->touch(); + else { + auto colors = info.prop->getValues(); + if(colors.size()!=shape.countSubShapes(info.type)) { + colors.clear(); + colors.resize(shape.countSubShapes(info.type),info.defaultColor); + } + for(auto &v : info.colors) { + if(v.first>=(int)colors.size()) + break; + colors[v.first] = v.second; + } + info.prop->setValue(colors); + } + continue; + } + int count = shape.countSubShapes(info.type); + bool touched = false; + std::vector colors(count,info.defaultColor); + auto it = info.colors.begin(); + const char * typeName = shape.shapeName(info.type).c_str(); + float trans = Transparency.getValue()/100.0f; + for(int i=0;ifirst) { + if(colors[i]!=it->second) { + touched = true; + colors[i] = it->second; + } + ++it; + continue; + } + Data::MappedName mapped = shape.getMappedName( + Data::IndexedName::fromConst(typeName, i+1)); + if(!mapped) + continue; + + App::Document *doc = sourceDoc; + if(geoFeature) { + auto owner = geoFeature->getElementOwner(mapped); + if(owner) + doc = owner->getDocument(); + } + auto color = getElementColor(info.defaultColor, shape, doc,info.type,mapped,caches); + if(!MapTransparency.getValue()) + color.a = trans; + if(color != colors[i]) { + touched = true; + colors[i] = color; + } + } + if(!touched) { + colors.clear(); + colors.push_back(info.defaultColor); + } + info.prop->setValue(colors); + } } void ViewProviderPartExt::updateData(const App::Property* prop) { - const char *propName = prop->getName(); - if (propName && (strcmp(propName, "Shape") == 0 || strstr(propName, "Touched") != nullptr)) { + const char *shapeProp = shapePropName.empty()?"Shape":shapePropName.c_str(); + const char *propName = prop?prop->getName():""; + if(strcmp(propName,"ColoredElements")==0 + || strcmp(propName,shapeProp)==0 + || strstr(propName,"Touched")!=0) + { + TopoDS_Shape cShape = getShape().getShape(); + if(cachedShape.getShape().IsPartner(cShape)) { + updateColors(); + Gui::ViewProviderGeometryObject::updateData(prop); + return; + } + // calculate the visual only if visible if (isUpdateForced() || Visibility.getValue()) updateVisual(); else VisualTouched = true; + updateColors(); + if (!VisualTouched) { if (this->faceset->partIndex.getNum() > this->pcShapeMaterial->diffuseColor.getNum()) { @@ -865,17 +1592,16 @@ void ViewProviderPartExt::setupContextMenu(QMenu* menu, QObject* receiver, const act->setData(QVariant((int)ViewProvider::Color)); } +void ViewProviderPartExt::setEditViewer(Gui::View3DInventorViewer *viewer, int ModNum) { + if (ModNum == ViewProvider::Color) + Gui::Control().showDialog(new Gui::TaskElementColors(this,true)); + else + Gui::ViewProviderGeometryObject::setEditViewer(viewer,ModNum); +} + bool ViewProviderPartExt::changeFaceColors() { - Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog(); - if (dlg) { - Gui::Control().showDialog(dlg); - return false; - } - - Gui::Selection().clearSelection(); - Gui::Control().showDialog(new TaskFaceColors(this)); - return true; + return Gui::Application::Instance->activeDocument()->setEdit(this, (int)ViewProvider::Color); } bool ViewProviderPartExt::setEdit(int ModNum) @@ -884,7 +1610,17 @@ bool ViewProviderPartExt::setEdit(int ModNum) // When double-clicking on the item for this pad the // object unsets and sets its edit mode without closing // the task panel - return changeFaceColors(); + Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog(); + if (dlg) { + Gui::Control().showDialog(dlg); + return false; + } + + // TaskFaceColors is replaced by TaskElementColors, and is inited in + // setEditViewer() in order to handle editing context + // + // Gui::Control().showDialog(new TaskFaceColors(this)); + return true; } else { return Gui::ViewProviderGeometryObject::setEdit(ModNum); @@ -903,6 +1639,14 @@ void ViewProviderPartExt::unsetEdit(int ModNum) void ViewProviderPartExt::updateVisual() { + if (!getObject() + || !getObject()->getDocument() + || isRestoring()) + { + VisualTouched = true; + return; + } + Gui::SoUpdateVBOAction action; action.apply(this->faceset); @@ -918,9 +1662,12 @@ void ViewProviderPartExt::updateVisual() haction.apply(this->lineset); haction.apply(this->nodeset); - TopoDS_Shape cShape = Part::Feature::getShape(getObject()); + const Part::TopoShape & toposhape = getShape(); + TopoDS_Shape cShape = toposhape.getShape(); + cachedShape = cShape; if (cShape.IsNull()) { coords ->point .setNum(0); + pcoords ->point .setNum(0); norm ->vector .setNum(0); faceset ->coordIndex .setNum(0); faceset ->partIndex .setNum(0); @@ -930,10 +1677,18 @@ void ViewProviderPartExt::updateVisual() return; } + // copy edge sub shape to work around OCC trangulation bug (in + // case the edge is part of a face of some other shape in a + // different location). Seems OCC 7.4 has fixed problem. +#if OCC_VERSION_HEX < 0x070400 + if (!toposhape.hasSubShape(TopAbs_FACE) && toposhape.hasSubShape(TopAbs_EDGE)) + cShape = BRepBuilderAPI_Copy(cShape).Shape(); +#endif + // time measurement and book keeping Base::TimeInfo start_time; - int numTriangles=0,numNodes=0,numNorms=0,numFaces=0,numEdges=0,numLines=0; - std::set faceEdges; + int numTriangles=0,numNodes=0,numPoints=0,numNorms=0,numFaces=0,numEdges=0,numLines=0; + std::unordered_map faceEdges; try { // calculating the deflection value @@ -962,7 +1717,8 @@ void ViewProviderPartExt::updateVisual() TopTools_IndexedMapOfShape faceMap; TopExp::MapShapes(cShape, TopAbs_FACE, faceMap); for (int i=1; i <= faceMap.Extent(); i++) { - Handle (Poly_Triangulation) mesh = BRep_Tool::Triangulation(TopoDS::Face(faceMap(i)), aLoc); + TopoDS_Face face = TopoDS::Face(faceMap(i)); + Handle (Poly_Triangulation) mesh = BRep_Tool::Triangulation(face, aLoc); if (mesh.IsNull()) { mesh = Part::Tools::triangulationOfFace(TopoDS::Face(faceMap(i))); } @@ -974,8 +1730,8 @@ void ViewProviderPartExt::updateVisual() } TopExp_Explorer xp; - for (xp.Init(faceMap(i),TopAbs_EDGE);xp.More();xp.Next()) - faceEdges.insert(xp.Current().HashCode(INT_MAX)); + for (xp.Init(face,TopAbs_EDGE);xp.More();xp.Next()) + faceEdges.emplace(xp.Current(), face); numFaces++; } @@ -1003,8 +1759,8 @@ void ViewProviderPartExt::updateVisual() // So, we have to store the hashes of the edges associated to a face. // If the hash of a given edge is not in this list we know it's really // a free edge. - int hash = aEdge.HashCode(INT_MAX); - if (faceEdges.find(hash) == faceEdges.end()) { + auto it = faceEdges.find(aEdge); + if (it == faceEdges.end()) { Handle(Poly_Polygon3D) aPoly = Part::Tools::polygonOfEdge(aEdge, aLoc); if (!aPoly.IsNull()) { int nbNodesInEdge = aPoly->NbNodes(); @@ -1013,11 +1769,6 @@ void ViewProviderPartExt::updateVisual() } } - // handling of the vertices - TopTools_IndexedMapOfShape vertexMap; - TopExp::MapShapes(cShape, TopAbs_VERTEX, vertexMap); - numNodes += vertexMap.Extent(); - // create memory for the nodes and indexes coords ->point .setNum(numNodes); norm ->vector .setNum(numNorms); @@ -1202,8 +1953,7 @@ void ViewProviderPartExt::updateVisual() TopLoc_Location aLoc; // handling of the free edge that are not associated to a face - int hash = aEdge.HashCode(INT_MAX); - if (faceEdges.find(hash) == faceEdges.end()) { + if (!faceEdges.count(aEdge)) { Handle(Poly_Polygon3D) aPoly = Part::Tools::polygonOfEdge(aEdge, aLoc); if (!aPoly.IsNull()) { if (!aLoc.IsIdentity()) { @@ -1229,11 +1979,18 @@ void ViewProviderPartExt::updateVisual() } } - nodeset->startIndex.setValue(faceNodeOffset); - for (int i=0; ipoint.setNum(numPoints); + verts = pcoords->point.startEditing(); + + for (int i=0; ipoint .finishEditing(); + pcoords ->point .finishEditing(); norm ->vector .finishEditing(); faceset ->coordIndex .finishEditing(); faceset ->partIndex .finishEditing(); lineset ->coordIndex .finishEditing(); } + catch (Base::Exception &e) { + FC_ERR("Failed to compute Inventor representation for the shape of " << pcObject->getFullName() << ": " << e.what()); + } catch (const Standard_Failure& e) { FC_ERR("Cannot compute Inventor representation for the shape of " << pcObject->getFullName() << ": " << e.GetMessageString()); @@ -1270,20 +2031,68 @@ void ViewProviderPartExt::updateVisual() FC_ERR("Cannot compute Inventor representation for the shape of " << pcObject->getFullName()); } -# ifdef FC_DEBUG - // printing some information - Base::Console().Log("ViewProvider update time: %f s\n",Base::TimeInfo::diffTimeF(start_time,Base::TimeInfo())); - Base::Console().Log("Shape tria info: Faces:%d Edges:%d Nodes:%d Triangles:%d IdxVec:%d\n",numFaces,numEdges,numNodes,numTriangles,numLines); -# endif + // printing some information + FC_TRACE(getFullName() << " update time: " << Base::TimeInfo::diffTimeF(start_time,Base::TimeInfo())); + FC_TRACE("Shape tria info: Faces:" << numFaces << " Edges:" << numEdges + << " Points:" << numPoints << " Nodes:" << numNodes + << " Triangles:" << numTriangles << " IdxVec:" << numLines); VisualTouched = false; + + // The material has to be checked again (#0001736) + setHighlightedFaces(DiffuseColor.getValues()); + setHighlightedEdges(LineColorArray.getValues()); + setHighlightedPoints(PointColorArray.getValue()); } + void ViewProviderPartExt::forceUpdate(bool enable) { if(enable) { if(++forceUpdateCount == 1) { - if(!isShow() && VisualTouched) + if (!isShow() && VisualTouched) updateVisual(); } }else if(forceUpdateCount) --forceUpdateCount; } +PyObject* ViewProviderPartExt::getPyObject() +{ + if (!pyViewObject) + pyViewObject = new ViewProviderPartExtPy(this); + pyViewObject->IncRef(); + return pyViewObject; +} + +void ViewProviderPartExt::beforeDelete() +{ + setStatus(Gui::Detach, true); + inherited::beforeDelete(); + // clear coin nodes to free up some memory + updateVisual(); +} + +void ViewProviderPartExt::reattach(App::DocumentObject *obj) +{ + inherited::reattach(obj); + if(isUpdateForced() || Visibility.getValue()) + updateVisual(); +} + +void ViewProviderPartExt::finishRestoring() +{ + inherited::finishRestoring(); + + auto syncMaterial = [](const App::Material &Mat, SoMaterial *pcMaterial) { + pcMaterial->ambientColor.setValue(Mat.ambientColor.r,Mat.ambientColor.g,Mat.ambientColor.b); + pcMaterial->specularColor.setValue(Mat.specularColor.r,Mat.specularColor.g,Mat.specularColor.b); + pcMaterial->emissiveColor.setValue(Mat.emissiveColor.r,Mat.emissiveColor.g,Mat.emissiveColor.b); + pcMaterial->shininess.setValue(Mat.shininess); + if (pcMaterial->diffuseColor.getNum() == 1) + pcMaterial->transparency.setValue(Mat.transparency); + }; + syncMaterial(LineMaterial.getValue(), pcLineMaterial); + syncMaterial(PointMaterial.getValue(), pcPointMaterial); + syncMaterial(ShapeMaterial.getValue(), pcShapeMaterial); + + if(VisualTouched && (isUpdateForced() || Visibility.getValue())) + updateVisual(); +} diff --git a/src/Mod/Part/Gui/ViewProviderExt.h b/src/Mod/Part/Gui/ViewProviderExt.h index 8be7f52c0a..f3c32f6e91 100644 --- a/src/Mod/Part/Gui/ViewProviderExt.h +++ b/src/Mod/Part/Gui/ViewProviderExt.h @@ -62,10 +62,12 @@ namespace PartGui { class SoBrepFaceSet; class SoBrepEdgeSet; class SoBrepPointSet; +class SoFCCoordinate3; class PartGuiExport ViewProviderPartExt : public Gui::ViewProviderGeometryObject { PROPERTY_HEADER_WITH_OVERRIDE(PartGui::ViewProviderPartExt); + typedef Gui::ViewProviderGeometryObject inherited; public: /// constructor @@ -92,6 +94,13 @@ public: // Faces (Gui::ViewProviderGeometryObject::ShapeColor and Gui::ViewProviderGeometryObject::ShapeMaterial apply) App::PropertyColorList DiffuseColor; + App::PropertyColorList MappedColors; + App::PropertyBool MapFaceColor; + App::PropertyBool MapLineColor; + App::PropertyBool MapPointColor; + App::PropertyBool MapTransparency; + App::PropertyBool ForceMapColors; + virtual void attach(App::DocumentObject *) override; virtual void setDisplayMode(const char* ModeName) override; /// returns a list of all possible modes @@ -103,6 +112,8 @@ public: virtual void updateData(const App::Property*) override; + virtual PyObject *getPyObject() override; + /** @name Selection handling * This group of methods do the selection handling. * Here you can define how the selection for your ViewProfider @@ -111,9 +122,13 @@ public: //@{ /// indicates if the ViewProvider use the new Selection model virtual bool useNewSelectionModel(void) const override {return true;} - /// return a hit element to the selection path or 0 - virtual std::string getElement(const SoDetail*) const override; + virtual bool getElementPicked(const SoPickedPoint *, std::string &subname) const override; + virtual std::string getElement(const SoDetail *detail) const override; virtual SoDetail* getDetail(const char*) const override; + virtual bool getDetailPath(const char *subname, + SoFullPath *pPath, + bool append, + SoDetail *&det) const override; virtual std::vector getModelPoints(const SoPickedPoint *) const override; /// return the highlight lines for a given element or the whole shape virtual std::vector getSelectionShape(const char* Element) const override; @@ -130,11 +145,13 @@ public: void unsetHighlightedEdges(); void setHighlightedPoints(const std::vector& colors); void unsetHighlightedPoints(); + //@} /** @name Color management methods */ //@{ + virtual void setElementColors(const std::map &colors) override; virtual std::map getElementColors(const char *element=nullptr) const override; //@} @@ -145,9 +162,27 @@ public: virtual bool allowOverride(const App::DocumentObject &) const override; + virtual void updateColors(App::Document *sourceDoc=0, bool forceColorMap=false) override; + + virtual void checkColorUpdate() override; + + static std::vector getShapeColors(const Part::TopoShape &shape, App::Color &defColor, + App::Document *sourceDoc=0, bool linkOnly=false); + /** @name Edit methods */ //@{ void setupContextMenu(QMenu*, QObject*, const char*) override; + virtual void setEditViewer(Gui::View3DInventorViewer*, int ModNum) override; + + virtual void setShapePropertyName(const char *propName); + const char *getShapePropertyName() const; + + Part::TopoShape getShape() const; + virtual void updateVisual(); + + virtual void reattach(App::DocumentObject *) override; + virtual void beforeDelete() override; + virtual void finishRestoring() override; protected: bool setEdit(int ModNum) override; @@ -157,8 +192,8 @@ protected: protected: /// get called by the container whenever a property has been changed virtual void onChanged(const App::Property* prop) override; - bool loadParameter(); - void updateVisual(); + + virtual bool hasBaseFeature() const; // nodes for the data representation SoMaterialBinding * pcFaceBind; @@ -171,6 +206,7 @@ protected: SoShapeHints * pShapeHints; SoCoordinate3 * coords; + SoCoordinate3 * pcoords; SoBrepFaceSet * faceset; SoNormal * norm; SoNormalBinding * normb; @@ -179,6 +215,11 @@ protected: bool VisualTouched; bool NormalsFromUV; + bool UpdatingColor; + + std::string shapePropName; + + friend class SoFCCoordinate3; private: // settings stuff @@ -188,6 +229,9 @@ private: static App::PropertyQuantityConstraint::Constraints angDeflectionRange; static const char* LightingEnums[]; static const char* DrawStyleEnums[]; + + Part::TopoShape cachedShape; + boost::signals2::scoped_connection conn; }; } diff --git a/src/Mod/Part/Gui/ViewProviderMirror.cpp b/src/Mod/Part/Gui/ViewProviderMirror.cpp index 31368f4394..332a556647 100644 --- a/src/Mod/Part/Gui/ViewProviderMirror.cpp +++ b/src/Mod/Part/Gui/ViewProviderMirror.cpp @@ -678,6 +678,11 @@ PROPERTY_SOURCE(PartGui::ViewProviderRefine, PartGui::ViewProviderPart) ViewProviderRefine::ViewProviderRefine() { sPixmap = "Part_Refine_Shape"; + ForceMapColors.setValue(true); + MapFaceColor.setValue(true); + MapLineColor.setValue(true); + MapPointColor.setValue(true); + MapTransparency.setValue(true); } ViewProviderRefine::~ViewProviderRefine() @@ -692,6 +697,12 @@ ViewProviderReverse::ViewProviderReverse() { //TODO: Need a specific icon here! //sPixmap = "Part_Reverse_Shape"; + + ForceMapColors.setValue(true); + MapFaceColor.setValue(true); + MapLineColor.setValue(true); + MapPointColor.setValue(true); + MapTransparency.setValue(true); } ViewProviderReverse::~ViewProviderReverse() diff --git a/src/Mod/Part/Gui/ViewProviderPartExtPy.xml b/src/Mod/Part/Gui/ViewProviderPartExtPy.xml new file mode 100644 index 0000000000..543b8a5111 --- /dev/null +++ b/src/Mod/Part/Gui/ViewProviderPartExtPy.xml @@ -0,0 +1,39 @@ + + + + + + This is the father of all Part ViewProvider classes + + + + +mapShapeColors(sourceDoc=None): Update shape colors based on the element map inside the shape + +sourceDoc: the document of the source object in case the shape comes from another document. + If None, then use the owner document + + + + + + Set/get shape property name + + + + + + Set/get element colors as a dict(name:color) + + + + + diff --git a/src/Mod/Part/Gui/ViewProviderPartExtPyImp.cpp b/src/Mod/Part/Gui/ViewProviderPartExtPyImp.cpp new file mode 100644 index 0000000000..67f3e2e157 --- /dev/null +++ b/src/Mod/Part/Gui/ViewProviderPartExtPyImp.cpp @@ -0,0 +1,98 @@ +/**************************************************************************** + * Copyright (c) 2018 Zheng Lei (realthunder) * + * * + * 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" + +#include +#include "ViewProviderExt.h" + +// inclusion of the generated files (generated out of ViewProviderPartExtPy.xml) +#include "ViewProviderPartExtPy.h" +#include "ViewProviderPartExtPy.cpp" + +using namespace PartGui; + +// returns a string which represent the object e.g. when printed in python +std::string ViewProviderPartExtPy::representation(void) const +{ + return std::string(""); +} + +PyObject *ViewProviderPartExtPy::getCustomAttributes(const char* ) const +{ + return 0; +} + +int ViewProviderPartExtPy::setCustomAttributes(const char* , PyObject *) +{ + return 0; +} + +PyObject *ViewProviderPartExtPy::mapShapeColors(PyObject *args) { + PyObject *pyDoc = Py_None; + if(!PyArg_ParseTuple(args, "|O!", &App::DocumentPy::Type,&pyDoc)) + return 0; + auto vp = getViewProviderPartExtPtr(); + App::Document *doc = 0; + if(pyDoc!=Py_None) + doc = static_cast(pyDoc)->getDocumentPtr(); + vp->updateColors(doc,true); + Py_Return; +} + +Py::Dict ViewProviderPartExtPy::getElementColors() const { + auto *vp = getViewProviderPartExtPtr(); + Py::Dict dict; + for(auto &v : vp->getElementColors()) { + Py::TupleN color(Py::Float(v.second.r),Py::Float(v.second.g), + Py::Float(v.second.b),Py::Float(v.second.a)); + dict[Py::String(v.first)] = color; + } + return dict; +} + +void ViewProviderPartExtPy::setElementColors(Py::Dict dict) { + auto *vp = getViewProviderPartExtPtr(); + std::map info; + for(auto it=dict.begin();it!=dict.end();++it) { + const auto &value = *it; + if(!value.first.isString()) + throw Py::TypeError("expect the key to be string"); + App::PropertyColor prop; + try { + prop.setPyObject(value.second.ptr()); + }catch(Base::Exception &e) { + e.setPyException(); + return; + } + info.emplace(Py::String(value.first).as_string(),prop.getValue()); + } + vp->setElementColors(info); +} + +Py::String ViewProviderPartExtPy::getShapePropertyName() const { + return Py::String(getViewProviderPartExtPtr()->getShapePropertyName()); +} + +void ViewProviderPartExtPy::setShapePropertyName(Py::String name) { + getViewProviderPartExtPtr()->setShapePropertyName(name.as_string().c_str()); +}