/*************************************************************************** * Copyright (c) 2004 Werner Mayer * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #include "PreCompiled.h" #ifndef _PreComp_ #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 #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 #include #include #include #include #include #include "SoFCIndexedFaceSet.h" #include "SoFCMeshObject.h" #include "ViewProvider.h" using namespace MeshGui; namespace sp = std::placeholders; using Mesh::Feature; using MeshCore::MeshFacet; using MeshCore::MeshFacetIterator; using MeshCore::MeshGeomFacet; using MeshCore::MeshKernel; using MeshCore::MeshPointIterator; void ViewProviderMeshBuilder::buildNodes(const App::Property* prop, std::vector& nodes) const { SoCoordinate3* pcPointsCoord = nullptr; SoIndexedFaceSet* pcFaces = nullptr; if (nodes.empty()) { pcPointsCoord = new SoCoordinate3(); nodes.push_back(pcPointsCoord); pcFaces = new SoIndexedFaceSet(); nodes.push_back(pcFaces); } else if (nodes.size() == 2) { if (nodes[0]->getTypeId() == SoCoordinate3::getClassTypeId()) { pcPointsCoord = static_cast(nodes[0]); } if (nodes[1]->getTypeId() == SoIndexedFaceSet::getClassTypeId()) { pcFaces = static_cast(nodes[1]); } } if (pcPointsCoord && pcFaces) { createMesh(prop, pcPointsCoord, pcFaces); } } void ViewProviderMeshBuilder::createMesh(const App::Property* prop, SoCoordinate3* coords, SoIndexedFaceSet* faces) const { const Mesh::PropertyMeshKernel* mesh = static_cast(prop); const MeshCore::MeshKernel& rcMesh = mesh->getValue().getKernel(); createMesh(rcMesh, coords, faces); } void ViewProviderMeshBuilder::createMesh(const MeshCore::MeshKernel& kernel, SoCoordinate3* coords, SoIndexedFaceSet* faces) const { // set the point coordinates const MeshCore::MeshPointArray& cP = kernel.GetPoints(); coords->point.setNum(kernel.CountPoints()); SbVec3f* verts = coords->point.startEditing(); int i = 0; for (MeshCore::MeshPointArray::_TConstIterator it = cP.begin(); it != cP.end(); ++it, i++) { verts[i].setValue(it->x, it->y, it->z); } coords->point.finishEditing(); // set the face indices int j = 0; const MeshCore::MeshFacetArray& cF = kernel.GetFacets(); faces->coordIndex.setNum(4 * kernel.CountFacets()); int32_t* indices = faces->coordIndex.startEditing(); for (MeshCore::MeshFacetArray::_TConstIterator it = cF.begin(); it != cF.end(); ++it, j++) { for (int i = 0; i < 3; i++) { indices[4 * j + i] = it->_aulPoints[i]; } indices[4 * j + 3] = SO_END_FACE_INDEX; } faces->coordIndex.finishEditing(); } PROPERTY_SOURCE(MeshGui::ViewProviderExport, Gui::ViewProviderDocumentObject) ViewProviderExport::ViewProviderExport() = default; ViewProviderExport::~ViewProviderExport() = default; std::vector ViewProviderExport::getDisplayModes() const { std::vector mode; mode.emplace_back(""); return mode; } const char* ViewProviderExport::getDefaultDisplayMode() const { return ""; } QIcon ViewProviderExport::getIcon() const { // clang-format off const char * Mesh_Feature_xpm[] = { "22 22 6 1", ". c None", "# c #000000", "c c #ffff00", "a c #808080", "b c #c0c0c0", "f c #008000", ".............##.......", ".............###......", ".............#f##.....", ".#....####...#ff##....", ".##.##....#..#fff##...", ".###.........#ffff##..", ".####........#fffff##.", ".#####.......#ffffff##", ".............#########", ".####.................", "#abab##########.......", "#babababababab#.......", "#ababababababa#.......", "#babab################", "#abab##cccccccccccc##.", "#bab##cccccccccccc##..", "#ab##cccccccccccc##...", "#b##cccccccccccc##....", "###cccccccccccc##.....", "##cccccccccccc##......", "###############.......", "......................"}; QPixmap px(Mesh_Feature_xpm); return px; // clang-format on } // ------------------------------------------------------ App::PropertyFloatConstraint::Constraints ViewProviderMesh::floatRange = {1.0F, 64.0F, 1.0F}; App::PropertyFloatConstraint::Constraints ViewProviderMesh::angleRange = {0.0F, 180.0F, 1.0F}; App::PropertyIntegerConstraint::Constraints ViewProviderMesh::intPercent = {0, 100, 5}; const char* ViewProviderMesh::LightingEnums[] = {"One side", "Two side", nullptr}; PROPERTY_SOURCE(MeshGui::ViewProviderMesh, Gui::ViewProviderGeometryObject) ViewProviderMesh::ViewProviderMesh() : highlightMode {HighlighMode::None} { // NOLINTBEGIN static const char* osgroup = "Object Style"; ADD_PROPERTY_TYPE(LineTransparency, (0), osgroup, App::Prop_None, "Set line transparency."); LineTransparency.setConstraints(&intPercent); ADD_PROPERTY_TYPE(LineWidth, (1.0f), osgroup, App::Prop_None, "Set line width."); LineWidth.setConstraints(&floatRange); ADD_PROPERTY_TYPE(PointSize, (2.0f), osgroup, App::Prop_None, "Set point size."); PointSize.setConstraints(&floatRange); ADD_PROPERTY_TYPE(CreaseAngle, (0.0f), osgroup, App::Prop_None, "Set crease angle."); CreaseAngle.setConstraints(&angleRange); ADD_PROPERTY_TYPE(OpenEdges, (false), osgroup, App::Prop_None, "Set open edges."); ADD_PROPERTY_TYPE(Coloring, (false), osgroup, App::Prop_None, "Set coloring."); ADD_PROPERTY_TYPE(Lighting, (1), osgroup, App::Prop_None, "Set if the illumination comes from two sides\n or one side in the 3D view."); Lighting.setEnums(LightingEnums); ADD_PROPERTY_TYPE(LineColor, (0, 0, 0), osgroup, App::Prop_None, "Set line color."); // Create the selection node pcHighlight = Gui::ViewProviderBuilder::createSelection(); pcHighlight->ref(); if (pcHighlight->selectionMode.getValue() == Gui::SoFCSelection::SEL_OFF) { Selectable.setValue(false); } pcShapeGroup = new SoGroup(); pcShapeGroup->ref(); pcHighlight->addChild(pcShapeGroup); pOpenColor = new SoBaseColor(); setOpenEdgeColorFrom(ShapeAppearance.getDiffuseColor()); pOpenColor->ref(); pcLineStyle = new SoDrawStyle(); pcLineStyle->ref(); pcLineStyle->style = SoDrawStyle::LINES; pcLineStyle->lineWidth = LineWidth.getValue(); pcPointStyle = new SoDrawStyle(); pcPointStyle->ref(); pcPointStyle->style = SoDrawStyle::POINTS; pcPointStyle->pointSize = PointSize.getValue(); pShapeHints = new SoShapeHints; pShapeHints->shapeType = SoShapeHints::UNKNOWN_SHAPE_TYPE; pShapeHints->ref(); pcMatBinding = new SoMaterialBinding; pcMatBinding->value = SoMaterialBinding::OVERALL; pcMatBinding->ref(); pLineColor = new SoMaterial; pLineColor->ref(); LineColor.touch(); // read the correct shape color from the preferences Base::Reference hGrp = Gui::WindowParameter::getDefaultParameter()->GetGroup("Mod/Mesh"); // Mesh color App::Color color = ShapeAppearance.getDiffuseColor(); unsigned long current = color.getPackedValue(); unsigned long setting = hGrp->GetUnsigned("MeshColor", current); if (current != setting) { color.setPackedValue((uint32_t)setting); ShapeAppearance.setDiffuseColor(color); } Transparency.setValue(hGrp->GetInt("MeshTransparency", 0)); // Line color color = LineColor.getValue(); current = color.getPackedValue(); setting = hGrp->GetUnsigned("LineColor", current); if (current != setting) { color.setPackedValue((uint32_t)setting); LineColor.setValue(color); } LineTransparency.setValue(hGrp->GetInt("LineTransparency", 0)); bool twoside = hGrp->GetBool("TwoSideRendering", false); if (twoside) { Lighting.setValue(1); } else { Lighting.setValue((long)0); } bool normal_per_vertex = hGrp->GetBool("VertexPerNormals", false); if (normal_per_vertex) { double angle = hGrp->GetFloat("CreaseAngle", 0.0); CreaseAngle.setValue(angle); } if (hGrp->GetBool("ShowBoundingBox", false)) { SelectionStyle.setValue(1); } Coloring.setStatus(App::Property::Hidden, true); // NOLINTEND } ViewProviderMesh::~ViewProviderMesh() { pcHighlight->unref(); pcShapeGroup->unref(); pOpenColor->unref(); pcLineStyle->unref(); pcPointStyle->unref(); pShapeHints->unref(); pcMatBinding->unref(); pLineColor->unref(); } void ViewProviderMesh::onChanged(const App::Property* prop) { // we're going to change the number of colors to one if (prop == &ShapeAppearance) { pcMatBinding->value = SoMaterialBinding::OVERALL; } if (prop == &LineTransparency) { float trans = LineTransparency.getValue() / 100.0F; pLineColor->transparency = trans; } else if (prop == &LineWidth) { pcLineStyle->lineWidth = LineWidth.getValue(); } else if (prop == &PointSize) { pcPointStyle->pointSize = PointSize.getValue(); } else if (prop == &CreaseAngle) { pShapeHints->creaseAngle = Base::toRadians(CreaseAngle.getValue()); } else if (prop == &OpenEdges) { showOpenEdges(OpenEdges.getValue()); } else if (prop == &Lighting) { if (Lighting.getValue() == 0) { pShapeHints->vertexOrdering = SoShapeHints::UNKNOWN_ORDERING; } else { pShapeHints->vertexOrdering = SoShapeHints::COUNTERCLOCKWISE; } } else if (prop == &LineColor) { const App::Color& c = LineColor.getValue(); pLineColor->diffuseColor.setValue(c.r, c.g, c.b); } else if (prop == &Coloring) { tryColorPerVertexOrFace(Coloring.getValue()); } else if (prop == &SelectionStyle) { pcHighlight->style = SelectionStyle.getValue() ? Gui::SoFCSelection::BOX : Gui::SoFCSelection::EMISSIVE; } else { // Set the inverse color for open edges if (prop == &ShapeAppearance) { setOpenEdgeColorFrom(ShapeAppearance.getDiffuseColor()); } } ViewProviderGeometryObject::onChanged(prop); } void ViewProviderMesh::setOpenEdgeColorFrom(const App::Color& c) { float r = 1.0F - c.r; r = r < 0.5F ? 0.0F : 1.0F; float g = 1.0F - c.g; g = g < 0.5F ? 0.0F : 1.0F; float b = 1.0F - c.b; b = b < 0.5F ? 0.0F : 1.0F; pOpenColor->rgb.setValue(r, g, b); } SoShape* ViewProviderMesh::getShapeNode() const { return nullptr; } SoNode* ViewProviderMesh::getCoordNode() const { return nullptr; } /** * Extracts the mesh data from the feature \a pcFeature and creates * an Inventor node \a SoNode with these data. */ void ViewProviderMesh::attach(App::DocumentObject* pcFeat) { ViewProviderGeometryObject::attach(pcFeat); pcHighlight->objectName = pcFeat->getNameInDocument(); pcHighlight->documentName = pcFeat->getDocument()->getName(); pcHighlight->subElementName = "Main"; // Note: Since for mesh data the SoFCSelection node has no SoSeparator but // an SoGroup as parent the EMISSIVE style if set has fundamentally no effect. // This behaviour is given due to the fact that SoFCSelection inherits from // SoGroup (formerly SoSeparator). If we wanted to enable emissive overlay for // highlighting or selection we would need an SoSeparator as parent node below. // faces SoGroup* pcFlatRoot = new SoGroup(); pcFlatRoot->addChild(pShapeHints); pcFlatRoot->addChild(pcShapeMaterial); pcFlatRoot->addChild(pcMatBinding); pcFlatRoot->addChild(pcHighlight); addDisplayMaskMode(pcFlatRoot, "Shaded"); // points SoGroup* pcPointRoot = new SoGroup(); pcPointRoot->addChild(pcPointStyle); pcPointRoot->addChild(pShapeHints); pcPointRoot->addChild(pcShapeMaterial); pcPointRoot->addChild(pcMatBinding); pcPointRoot->addChild(pcHighlight); addDisplayMaskMode(pcPointRoot, "Point"); // wires SoLightModel* pcLightModel = new SoLightModel(); pcLightModel->model = SoLightModel::BASE_COLOR; SoGroup* pcWireRoot = new SoGroup(); pcWireRoot->addChild(pcLineStyle); pcWireRoot->addChild(pcLightModel); SoMaterialBinding* binding = new SoMaterialBinding; binding->value = SoMaterialBinding::OVERALL; // doesn't set several colors pcWireRoot->addChild(binding); pcWireRoot->addChild(pLineColor); pcWireRoot->addChild(pcHighlight); addDisplayMaskMode(pcWireRoot, "Wireframe"); // faces+wires // Avoid any Z-buffer artifacts, so that the lines always // appear on top of the faces SoPolygonOffset* offset = new SoPolygonOffset(); offset->styles = SoPolygonOffset::FILLED; offset->factor = 1.0F; offset->units = 1.0F; SoSeparator* pcWireSep = new SoSeparator(); pcWireSep->addChild(pcLineStyle); pcWireSep->addChild(pcLightModel); pcWireSep->addChild(binding); pcWireSep->addChild(pLineColor); pcWireSep->addChild(pcHighlight); SoGroup* pcFlatWireRoot = new SoGroup(); pcFlatWireRoot->addChild(pcWireSep); pcFlatWireRoot->addChild(offset); pcFlatWireRoot->addChild(pShapeHints); pcFlatWireRoot->addChild(pcShapeMaterial); pcFlatWireRoot->addChild(pcMatBinding); pcFlatWireRoot->addChild(pcShapeGroup); addDisplayMaskMode(pcFlatWireRoot, "Flat Lines"); if (getColorProperty() || getMaterialProperty()) { Coloring.setStatus(App::Property::Hidden, false); } } void ViewProviderMesh::updateData(const App::Property* prop) { Gui::ViewProviderGeometryObject::updateData(prop); if (prop->is()) { Coloring.setStatus(App::Property::Hidden, false); } else if (prop->is()) { Coloring.setStatus(App::Property::Hidden, false); } } void ViewProviderMesh::finishRestoring() { if (Coloring.getValue()) { Coloring.touch(); } Gui::ViewProviderGeometryObject::finishRestoring(); } QIcon ViewProviderMesh::getIcon() const { static QIcon icon = Gui::BitmapFactory().pixmap("Mesh_Tree"); return icon; } App::PropertyColorList* ViewProviderMesh::getColorProperty() const { if (pcObject) { std::map Map; pcObject->getPropertyMap(Map); for (const auto& it : Map) { Base::Type type = it.second->getTypeId(); if (type == App::PropertyColorList::getClassTypeId()) { App::PropertyColorList* colors = static_cast(it.second); return colors; } } } return nullptr; // no such property found } void ViewProviderMesh::tryColorPerVertexOrFace(bool on) { if (on) { const Mesh::PropertyMeshKernel& meshProp = static_cast(pcObject)->Mesh; const Mesh::MeshObject& mesh = meshProp.getValue(); int numPoints = static_cast(mesh.countPoints()); int numFacets = static_cast(mesh.countFacets()); if (App::PropertyColorList* colors = getColorProperty()) { if (colors->getSize() == numPoints) { setColorPerVertex(colors); } else if (colors->getSize() == numFacets) { setColorPerFace(colors); } } else if (Mesh::PropertyMaterial* material = getMaterialProperty()) { auto bind = material->getBinding(); if (bind == MeshCore::MeshIO::Binding::OVERALL) { pcMatBinding->value = SoMaterialBinding::OVERALL; if (!material->getDiffuseColor().empty()) { auto c = material->getDiffuseColor()[0]; pcShapeMaterial->diffuseColor.setValue(c.r, c.g, c.b); } if (!material->getTransparency().empty()) { pcShapeMaterial->transparency.setValue(material->getTransparency()[0]); } } else if (bind == MeshCore::MeshIO::Binding::PER_VERTEX) { if (material->getDiffuseColor().size() == std::size_t(numPoints)) { pcMatBinding->value = SoMaterialBinding::PER_VERTEX_INDEXED; setDiffuseColor(material->getDiffuseColor()); } } else if (bind == MeshCore::MeshIO::Binding::PER_FACE) { if (material->getAmbientColor().size() == std::size_t(numFacets)) { pcMatBinding->value = SoMaterialBinding::PER_FACE; setAmbientColor(material->getAmbientColor()); } if (material->getDiffuseColor().size() == std::size_t(numFacets)) { pcMatBinding->value = SoMaterialBinding::PER_FACE; setDiffuseColor(material->getDiffuseColor()); } if (material->getEmissiveColor().size() == std::size_t(numFacets)) { pcMatBinding->value = SoMaterialBinding::PER_FACE; setEmissiveColor(material->getEmissiveColor()); } if (material->getSpecularColor().size() == std::size_t(numFacets)) { pcMatBinding->value = SoMaterialBinding::PER_FACE; setSpecularColor(material->getSpecularColor()); } if (material->getTransparency().size() == std::size_t(numFacets)) { pcMatBinding->value = SoMaterialBinding::PER_FACE; setFacetTransparency(material->getTransparency()); } } } } else { pcMatBinding->value = SoMaterialBinding::OVERALL; const App::Color& c = ShapeAppearance.getDiffuseColor(); pcShapeMaterial->diffuseColor.setValue(c.r, c.g, c.b); pcShapeMaterial->transparency.setValue(Transparency.getValue() / 100.0f); } } void ViewProviderMesh::setColorPerVertex(const App::PropertyColorList* prop) { pcMatBinding->value = SoMaterialBinding::PER_VERTEX_INDEXED; setDiffuseColor(prop->getValues()); } void ViewProviderMesh::setColorPerFace(const App::PropertyColorList* prop) { pcMatBinding->value = SoMaterialBinding::PER_FACE; setDiffuseColor(prop->getValues()); } void ViewProviderMesh::setColorField(const std::vector& val, SoMFColor& field) { field.setNum(val.size()); SbColor* col = field.startEditing(); std::size_t i = 0; for (auto it : val) { col[i++].setValue(it.r, it.g, it.b); } field.finishEditing(); } void ViewProviderMesh::setAmbientColor(const std::vector& val) { setColorField(val, pcShapeMaterial->ambientColor); } void ViewProviderMesh::setDiffuseColor(const std::vector& val) { setColorField(val, pcShapeMaterial->diffuseColor); } void ViewProviderMesh::setSpecularColor(const std::vector& val) { setColorField(val, pcShapeMaterial->specularColor); } void ViewProviderMesh::setEmissiveColor(const std::vector& val) { setColorField(val, pcShapeMaterial->emissiveColor); } Mesh::PropertyMaterial* ViewProviderMesh::getMaterialProperty() const { if (pcObject) { std::map Map; pcObject->getPropertyMap(Map); for (const auto& it : Map) { Base::Type type = it.second->getTypeId(); if (type == Mesh::PropertyMaterial::getClassTypeId()) { Mesh::PropertyMaterial* material = static_cast(it.second); return material; } } } return nullptr; // no such property found } void ViewProviderMesh::setDisplayMode(const char* ModeName) { if (strcmp("Shaded", ModeName) == 0) { setDisplayMaskMode("Shaded"); } else if (strcmp("Points", ModeName) == 0) { setDisplayMaskMode("Point"); } else if (strcmp("Flat Lines", ModeName) == 0) { setDisplayMaskMode("Flat Lines"); } else if (strcmp("Wireframe", ModeName) == 0) { setDisplayMaskMode("Wireframe"); } ViewProviderGeometryObject::setDisplayMode(ModeName); } std::vector ViewProviderMesh::getDisplayModes() const { std::vector StrList; // add your own modes StrList.emplace_back("Shaded"); StrList.emplace_back("Wireframe"); StrList.emplace_back("Flat Lines"); StrList.emplace_back("Points"); return StrList; } bool ViewProviderMesh::exportToVrml(const char* filename, const MeshCore::Material& mat, bool binary) const { SoCoordinate3* coords = new SoCoordinate3(); SoIndexedFaceSet* faces = new SoIndexedFaceSet(); ViewProviderMeshBuilder builder; builder.createMesh(&static_cast(pcObject)->Mesh, coords, faces); SoMaterialBinding* binding = new SoMaterialBinding; SoMaterial* material = new SoMaterial; if (static_cast(mat.diffuseColor.size()) == coords->point.getNum()) { binding->value = SoMaterialBinding::PER_VERTEX_INDEXED; } else if (static_cast(mat.diffuseColor.size()) == faces->coordIndex.getNum() / 4) { binding->value = SoMaterialBinding::PER_FACE_INDEXED; } if (mat.diffuseColor.size() > 1) { material->diffuseColor.setNum(mat.diffuseColor.size()); SbColor* colors = material->diffuseColor.startEditing(); for (unsigned int i = 0; i < mat.diffuseColor.size(); i++) { colors[i].setValue(mat.diffuseColor[i].r, mat.diffuseColor[i].g, mat.diffuseColor[i].b); } material->diffuseColor.finishEditing(); } SoGroup* group = new SoGroup(); group->addChild(material); group->addChild(binding); group->addChild(new SoTransform()); group->addChild(coords); group->addChild(faces); SoToVRML2Action tovrml2; group->ref(); tovrml2.apply(group); group->unref(); SoVRMLGroup* vrmlRoot = tovrml2.getVRML2SceneGraph(); vrmlRoot->ref(); std::string buffer = Gui::SoFCDB::writeNodesToString(vrmlRoot); vrmlRoot->unref(); // release the memory as soon as possible Base::FileInfo fi(filename); if (binary) { Base::ofstream str(fi, std::ios::out | std::ios::binary); zipios::GZIPOutputStream gzip(str); if (gzip) { gzip << buffer; gzip.close(); return true; } } else { Base::ofstream str(fi, std::ios::out); if (str) { str << buffer; str.close(); return true; } } return false; } void ViewProviderMesh::exportMesh(const char* filename, const char* fmt) const { MeshCore::MeshIO::Format format = MeshCore::MeshIO::Undefined; if (fmt) { std::string dummy = "meshfile."; dummy += fmt; format = MeshCore::MeshOutput::GetFormat(dummy.c_str()); } MeshCore::Material mat; int numColors = pcShapeMaterial->diffuseColor.getNum(); const SbColor* colors = pcShapeMaterial->diffuseColor.getValues(0); mat.diffuseColor.reserve(numColors); for (int i = 0; i < numColors; i++) { const SbColor& c = colors[i]; mat.diffuseColor.emplace_back(c[0], c[1], c[2]); } Mesh::MeshObject mesh = static_cast(getObject())->Mesh.getValue(); mesh.setPlacement(static_cast(getObject())->globalPlacement()); if (mat.diffuseColor.size() == mesh.countPoints()) { mat.binding = MeshCore::MeshIO::PER_VERTEX; } else if (mat.diffuseColor.size() == mesh.countFacets()) { mat.binding = MeshCore::MeshIO::PER_FACE; } else { mat.binding = MeshCore::MeshIO::OVERALL; } mesh.save(filename, format, &mat, getObject()->Label.getValue()); } void ViewProviderMesh::setupContextMenu(QMenu* menu, QObject* receiver, const char* member) { ViewProviderGeometryObject::setupContextMenu(menu, receiver, member); // toggle command to display components Gui::ActionFunction* func = new Gui::ActionFunction(menu); QAction* act = menu->addAction(QObject::tr("Display components")); act->setCheckable(true); act->setChecked(pcMatBinding->value.getValue() == SoMaterialBinding::PER_FACE && highlightMode == HighlighMode::Component); func->toggle(act, [this](bool on) { this->setHighlightedComponents(on); }); QAction* seg = menu->addAction(QObject::tr("Display segments")); seg->setCheckable(true); seg->setChecked(pcMatBinding->value.getValue() == SoMaterialBinding::PER_FACE && highlightMode == HighlighMode::Segment); func->toggle(seg, [this](bool on) { this->setHighlightedSegments(on); }); QAction* col = menu->addAction(QObject::tr("Display colors")); col->setVisible(canHighlightColors()); col->setCheckable(true); col->setChecked(highlightMode == HighlighMode::Color); func->toggle(col, [this](bool on) { this->setHighlightedColors(on); }); } bool ViewProviderMesh::setEdit(int ModNum) { if (ModNum == ViewProvider::Transform) { return ViewProviderGeometryObject::setEdit(ModNum); } else if (ModNum == ViewProvider::Color) { highlightComponents(); } return true; } void ViewProviderMesh::unsetEdit(int ModNum) { if (ModNum == ViewProvider::Transform) { ViewProviderGeometryObject::unsetEdit(ModNum); } else if (ModNum == ViewProvider::Color) { unhighlightSelection(); } } bool ViewProviderMesh::createToolMesh(const std::vector& rclPoly, const SbViewVolume& vol, const Base::Vector3f& rcNormal, std::vector& aFaces) { float fX {}, fY {}, fZ {}; SbVec3f pt1, pt2, pt3, pt4; MeshGeomFacet face; std::vector top, bottom, polygon; for (std::vector::const_iterator it = rclPoly.begin(); it != rclPoly.end(); ++it) { // the following element std::vector::const_iterator nt = it + 1; if (nt == rclPoly.end()) { nt = rclPoly.begin(); } else if (*it == *nt) { continue; // two adjacent vertices are equal } vol.projectPointToLine(*it, pt1, pt2); vol.projectPointToLine(*nt, pt3, pt4); // 1st facet pt1.getValue(fX, fY, fZ); face._aclPoints[0].Set(fX, fY, fZ); pt4.getValue(fX, fY, fZ); face._aclPoints[1].Set(fX, fY, fZ); pt3.getValue(fX, fY, fZ); face._aclPoints[2].Set(fX, fY, fZ); if (face.Area() > 0) { aFaces.push_back(face); } // 2nd facet pt1.getValue(fX, fY, fZ); face._aclPoints[0].Set(fX, fY, fZ); pt2.getValue(fX, fY, fZ); face._aclPoints[1].Set(fX, fY, fZ); pt4.getValue(fX, fY, fZ); face._aclPoints[2].Set(fX, fY, fZ); if (face.Area() > 0) { aFaces.push_back(face); } if (it + 1 < rclPoly.end()) { pt1.getValue(fX, fY, fZ); top.emplace_back(fX, fY, fZ); pt2.getValue(fX, fY, fZ); bottom.emplace_back(fX, fY, fZ); // polygon we need to triangulate (in x,y-plane) it->getValue(fX, fY); polygon.emplace_back(fX, fY, 0.0f); } } // now create the lids std::vector aLid; MeshCore::EarClippingTriangulator cTria; cTria.SetPolygon(polygon); bool ok = cTria.TriangulatePolygon(); std::vector faces = cTria.GetFacets(); for (const auto& face : faces) { MeshGeomFacet topFacet; topFacet._aclPoints[0] = top[face._aulPoints[0]]; topFacet._aclPoints[1] = top[face._aulPoints[1]]; topFacet._aclPoints[2] = top[face._aulPoints[2]]; if (topFacet.GetNormal() * rcNormal < 0) { std::swap(topFacet._aclPoints[1], topFacet._aclPoints[2]); topFacet.CalcNormal(); } aFaces.push_back(topFacet); MeshGeomFacet botFacet; botFacet._aclPoints[0] = bottom[face._aulPoints[0]]; botFacet._aclPoints[1] = bottom[face._aulPoints[1]]; botFacet._aclPoints[2] = bottom[face._aulPoints[2]]; if (botFacet.GetNormal() * rcNormal > 0) { std::swap(botFacet._aclPoints[1], botFacet._aclPoints[2]); botFacet.CalcNormal(); } aFaces.push_back(botFacet); } return ok; } void ViewProviderMesh::showOpenEdges(bool show) { (void)show; } namespace MeshGui { class MeshSplit { public: MeshSplit(ViewProviderMesh* mesh, std::vector poly, const Gui::ViewVolumeProjection& proj) : mesh(mesh) , poly(std::move(poly)) , proj(proj) {} void cutMesh() { Gui::Document* gui = mesh->getDocument(); gui->openCommand(QT_TRANSLATE_NOOP("Command", "Cut")); ViewProviderMesh* copy = makeCopy(); mesh->cutMesh(poly, proj, false); copy->cutMesh(poly, proj, true); gui->commitCommand(); delete this; } void trimMesh() { Gui::Document* gui = mesh->getDocument(); gui->openCommand(QT_TRANSLATE_NOOP("Command", "Trim")); ViewProviderMesh* copy = makeCopy(); mesh->trimMesh(poly, proj, false); copy->trimMesh(poly, proj, true); gui->commitCommand(); delete this; } ViewProviderMesh* makeCopy() const { Gui::Document* gui = mesh->getDocument(); App::Document* doc = gui->getDocument(); Mesh::Feature* cpy = static_cast(doc->addObject("Mesh::Feature")); Mesh::Feature* org = static_cast(mesh->getObject()); cpy->Label.setValue(org->Label.getValue()); cpy->Mesh.setValue(org->Mesh.getValue()); return static_cast(gui->getViewProvider(cpy)); } private: ViewProviderMesh* mesh; std::vector poly; Gui::ViewVolumeProjection proj; }; } // namespace MeshGui void ViewProviderMesh::clipMeshCallback(void* ud, SoEventCallback* n) { // show the wait cursor because this could take quite some time Gui::WaitCursor wc; // When this callback function is invoked we must in either case leave the edit mode Gui::View3DInventorViewer* view = static_cast(n->getUserData()); view->setEditing(false); view->removeEventCallback(SoMouseButtonEvent::getClassTypeId(), clipMeshCallback, ud); n->setHandled(); Gui::SelectionRole role {}; std::vector clPoly = view->getGLPolygon(&role); if (clPoly.size() < 3) { return; } if (clPoly.front() != clPoly.back()) { clPoly.push_back(clPoly.front()); } std::vector views = view->getViewProvidersOfType(ViewProviderMesh::getClassTypeId()); if (!views.empty()) { Gui::Application::Instance->activeDocument()->openCommand( QT_TRANSLATE_NOOP("Command", "Cut")); bool commitCommand = false; for (auto it : views) { ViewProviderMesh* self = static_cast(it); if (self->getEditingMode() > -1) { self->finishEditing(); SoCamera* cam = view->getSoRenderManager()->getCamera(); SbViewVolume vv = cam->getViewVolume(); Gui::ViewVolumeProjection proj(vv); proj.setTransform(static_cast(self->getObject()) ->Placement.getValue() .toMatrix()); if (role == Gui::SelectionRole::Inner) { self->cutMesh(clPoly, proj, true); commitCommand = true; } else if (role == Gui::SelectionRole::Outer) { self->cutMesh(clPoly, proj, false); commitCommand = true; } else if (role == Gui::SelectionRole::Split) { // We must delay the split because it adds a new // node to the scenegraph which cannot be done while // traversing it Gui::TimerFunction* func = new Gui::TimerFunction(); func->setAutoDelete(true); MeshSplit* split = new MeshSplit(self, clPoly, proj); func->setFunction([split]() { split->cutMesh(); }); func->singleShot(0); } } } if (commitCommand) { Gui::Application::Instance->activeDocument()->commitCommand(); } else { Gui::Application::Instance->activeDocument()->abortCommand(); } view->redraw(); } } void ViewProviderMesh::trimMeshCallback(void* ud, SoEventCallback* n) { // show the wait cursor because this could take quite some time Gui::WaitCursor wc; // When this callback function is invoked we must in either case leave the edit mode Gui::View3DInventorViewer* view = static_cast(n->getUserData()); view->setEditing(false); view->removeEventCallback(SoMouseButtonEvent::getClassTypeId(), trimMeshCallback, ud); n->setHandled(); Gui::SelectionRole role {}; std::vector clPoly = view->getGLPolygon(&role); if (clPoly.size() < 3) { return; } if (clPoly.front() != clPoly.back()) { clPoly.push_back(clPoly.front()); } std::vector views = view->getViewProvidersOfType(ViewProviderMesh::getClassTypeId()); if (!views.empty()) { Gui::Application::Instance->activeDocument()->openCommand( QT_TRANSLATE_NOOP("Command", "Trim")); bool commitCommand = false; for (auto it : views) { ViewProviderMesh* self = static_cast(it); if (self->getEditingMode() > -1) { self->finishEditing(); SoCamera* cam = view->getSoRenderManager()->getCamera(); SbViewVolume vv = cam->getViewVolume(); Gui::ViewVolumeProjection proj(vv); proj.setTransform(static_cast(self->getObject()) ->Placement.getValue() .toMatrix()); if (role == Gui::SelectionRole::Inner) { self->trimMesh(clPoly, proj, true); commitCommand = true; } else if (role == Gui::SelectionRole::Outer) { self->trimMesh(clPoly, proj, false); commitCommand = true; } else if (role == Gui::SelectionRole::Split) { // We must delay the split because it adds a new // node to the scenegraph which cannot be done while // traversing it Gui::TimerFunction* func = new Gui::TimerFunction(); func->setAutoDelete(true); MeshSplit* split = new MeshSplit(self, clPoly, proj); func->setFunction([split]() { split->trimMesh(); }); func->singleShot(0); } } } if (commitCommand) { Gui::Application::Instance->activeDocument()->commitCommand(); } else { Gui::Application::Instance->activeDocument()->abortCommand(); } view->redraw(); } } void ViewProviderMesh::partMeshCallback(void* ud, SoEventCallback* cb) { // show the wait cursor because this could take quite some time Gui::WaitCursor wc; // When this callback function is invoked we must in either case leave the edit mode Gui::View3DInventorViewer* view = static_cast(cb->getUserData()); view->setEditing(false); view->removeEventCallback(SoMouseButtonEvent::getClassTypeId(), partMeshCallback, ud); cb->setHandled(); Gui::SelectionRole role {}; std::vector clPoly = view->getGLPolygon(&role); if (clPoly.size() < 3) { return; } if (clPoly.front() != clPoly.back()) { clPoly.push_back(clPoly.front()); } // get the normal of the front clipping plane SbVec3f b, n; view->getNearPlane(b, n); Base::Vector3f cNormal(n[0], n[1], n[2]); SoCamera* pCam = view->getSoRenderManager()->getCamera(); SbViewVolume vol = pCam->getViewVolume(); // create a tool shape from these points std::vector aFaces; if (!ViewProviderMesh::createToolMesh(clPoly, vol, cNormal, aFaces)) { Base::Console().Message("The picked polygon seems to have self-overlappings. This could " "lead to strange results."); } MeshCore::MeshKernel toolMesh; bool locked = Base::Sequencer().setLocked(true); toolMesh = aFaces; Base::Sequencer().setLocked(locked); // Open a transaction object for the undo/redo stuff Gui::Application::Instance->activeDocument()->openCommand( QT_TRANSLATE_NOOP("Command", "Split")); try { std::vector views = view->getViewProvidersOfType(ViewProviderMesh::getClassTypeId()); for (auto view : views) { ViewProviderMesh* that = static_cast(view); if (that->getEditingMode() > -1) { that->finishEditing(); Base::Placement plm = static_cast(that->getObject())->Placement.getValue(); plm.invert(); MeshCore::MeshKernel copyToolMesh(toolMesh); copyToolMesh.Transform(plm.toMatrix()); if (role == Gui::SelectionRole::Inner) { that->splitMesh(copyToolMesh, cNormal, true); } else { that->splitMesh(copyToolMesh, cNormal, false); } } } } catch (...) { // Don't rethrow any exception } // Close the transaction Gui::Application::Instance->activeDocument()->commitCommand(); view->redraw(); } void ViewProviderMesh::segmMeshCallback(void* ud, SoEventCallback* cb) { // show the wait cursor because this could take quite some time Gui::WaitCursor wc; // When this callback function is invoked we must in either case leave the edit mode Gui::View3DInventorViewer* view = static_cast(cb->getUserData()); view->setEditing(false); view->removeEventCallback(SoMouseButtonEvent::getClassTypeId(), segmMeshCallback, ud); cb->setHandled(); Gui::SelectionRole role {}; std::vector clPoly = view->getGLPolygon(&role); if (clPoly.size() < 3) { return; } if (clPoly.front() != clPoly.back()) { clPoly.push_back(clPoly.front()); } // get the normal of the front clipping plane SbVec3f b, n; view->getNearPlane(b, n); Base::Vector3f cNormal(n[0], n[1], n[2]); SoCamera* pCam = view->getSoRenderManager()->getCamera(); SbViewVolume vol = pCam->getViewVolume(); // create a tool shape from these points std::vector aFaces; if (!ViewProviderMesh::createToolMesh(clPoly, vol, cNormal, aFaces)) { Base::Console().Message("The picked polygon seems to have self-overlappings. This could " "lead to strange results."); } MeshCore::MeshKernel toolMesh; bool locked = Base::Sequencer().setLocked(true); toolMesh = aFaces; Base::Sequencer().setLocked(locked); // Open a transaction object for the undo/redo stuff Gui::Application::Instance->activeDocument()->openCommand( QT_TRANSLATE_NOOP("Command", "Segment")); try { std::vector views = view->getViewProvidersOfType(ViewProviderMesh::getClassTypeId()); for (auto view : views) { ViewProviderMesh* that = static_cast(view); if (that->getEditingMode() > -1) { that->finishEditing(); Base::Placement plm = static_cast(that->getObject())->Placement.getValue(); plm.invert(); MeshCore::MeshKernel copyToolMesh(toolMesh); copyToolMesh.Transform(plm.toMatrix()); if (role == Gui::SelectionRole::Inner) { that->segmentMesh(copyToolMesh, cNormal, true); } else { that->segmentMesh(copyToolMesh, cNormal, false); } } } } catch (...) { // Don't rethrow any exception } // Close the transaction Gui::Application::Instance->activeDocument()->commitCommand(); view->redraw(); } void ViewProviderMesh::selectGLCallback(void* ud, SoEventCallback* n) { // When this callback function is invoked we must in either case leave the edit mode Gui::View3DInventorViewer* view = static_cast(n->getUserData()); view->setEditing(false); view->removeEventCallback(SoMouseButtonEvent::getClassTypeId(), selectGLCallback, ud); n->setHandled(); std::vector clPoly = view->getGLPolygon(); if (clPoly.size() != 2) { return; } const SoEvent* ev = n->getEvent(); SbVec2f pos = clPoly[0]; float pX {}, pY {}; pos.getValue(pX, pY); const SbVec2s& sz = view->getSoRenderManager()->getViewportRegion().getViewportSizePixels(); float fRatio = view->getSoRenderManager()->getViewportRegion().getViewportAspectRatio(); if (fRatio > 1.0F) { pX = (pX - 0.5F) / fRatio + 0.5F; pos.setValue(pX, pY); } else if (fRatio < 1.0F) { pY = (pY - 0.5F) * fRatio + 0.5F; pos.setValue(pX, pY); } short x1 = (short)(pX * sz[0] + 0.5F); short y1 = (short)(pY * sz[1] + 0.5F); SbVec2s loc = ev->getPosition(); short x2 = loc[0]; short y2 = loc[1]; short x = (x1 + x2) / 2; short y = (y1 + y2) / 2; short w = (x2 - x1); short h = (y2 - y1); if (w < 0) { w = -w; } if (h < 0) { h = -h; } std::vector views; views = view->getViewProvidersOfType(ViewProviderMesh::getClassTypeId()); for (auto it : views) { ViewProviderMesh* that = static_cast(it); if (that->getEditingMode() > -1) { that->finishEditing(); that->selectArea(x, y, w, h, view->getSoRenderManager()->getViewportRegion(), view->getSoRenderManager()->getCamera()); } } view->redraw(); } void ViewProviderMesh::getFacetsFromPolygon(const std::vector& picked, const Base::ViewProjMethod& proj, SbBool inner, std::vector& indices) const { const bool ok = true; Base::Polygon2d polygon; for (auto it : picked) { polygon.Add(Base::Vector2d(it[0], it[1])); } // Get the attached mesh property Mesh::PropertyMeshKernel& meshProp = static_cast(pcObject)->Mesh; MeshCore::MeshAlgorithm cAlg(meshProp.getValue().getKernel()); cAlg.CheckFacets(&proj, polygon, true, indices); if (!inner) { // get the indices that are completely outside std::vector complete(meshProp.getValue().countFacets()); std::generate(complete.begin(), complete.end(), Base::iotaGen(0)); std::sort(indices.begin(), indices.end()); std::vector complementary; std::back_insert_iterator> biit(complementary); std::set_difference(complete.begin(), complete.end(), indices.begin(), indices.end(), biit); indices = complementary; } if (!ok) { // note: the mouse grabbing needs to be released Base::Console().Message("The picked polygon seems to have self-overlappings. This could " "lead to strange results."); } } std::vector ViewProviderMesh::getFacetsOfRegion(const SbViewportRegion& select, const SbViewportRegion& region, SoCamera* camera) const { SoSeparator* root = new SoSeparator(); root->ref(); root->addChild(camera); root->addChild(this->getCoordNode()); root->addChild(this->getShapeNode()); Gui::SoGLSelectAction gl(region, select); gl.apply(root); root->unref(); std::vector faces; faces.insert(faces.end(), gl.indices.begin(), gl.indices.end()); return faces; } void ViewProviderMesh::panCamera(SoCamera* cam, float aspectratio, const SbPlane& panplane, const SbVec2f& currpos, const SbVec2f& prevpos) { if (!cam) { // can happen for empty scenegraph return; } if (currpos == prevpos) { // useless invocation return; } // Find projection points for the last and current mouse coordinates. SbViewVolume vv = cam->getViewVolume(aspectratio); SbLine line; vv.projectPointToLine(currpos, line); SbVec3f current_planept; panplane.intersect(line, current_planept); vv.projectPointToLine(prevpos, line); SbVec3f old_planept; panplane.intersect(line, old_planept); // Reposition camera according to the vector difference between the // projected points. cam->position = cam->position.getValue() - (current_planept - old_planept); } void ViewProviderMesh::boxZoom(const SbBox2s& box, const SbViewportRegion& vp, SoCamera* cam) { SbViewVolume vv = cam->getViewVolume(vp.getViewportAspectRatio()); short sizeX {}, sizeY {}; box.getSize(sizeX, sizeY); SbVec2s size = vp.getViewportSizePixels(); // The bbox must not be empty i.e. width and length is zero, but it is possible that // either width or length is zero if (sizeX == 0 && sizeY == 0) { return; } // Get the new center in normalized pixel coordinates short xmin {}; short xmax {}; short ymin {}; short ymax {}; box.getBounds(xmin, ymin, xmax, ymax); const SbVec2f center((float)((xmin + xmax) / 2) / (float)std::max((int)(size[0] - 1), 1), (float)(size[1] - (ymin + ymax) / 2) / (float)std::max((int)(size[1] - 1), 1)); SbPlane plane = vv.getPlane(cam->focalDistance.getValue()); panCamera(cam, vp.getViewportAspectRatio(), plane, SbVec2f(0.5, 0.5), center); // Set height or height angle of the camera float scaleX = (float)sizeX / (float)size[0]; float scaleY = (float)sizeY / (float)size[1]; float scale = std::max(scaleX, scaleY); if (cam->getTypeId() == SoOrthographicCamera::getClassTypeId()) { float height = static_cast(cam)->height.getValue() * scale; static_cast(cam)->height = height; } else if (cam->getTypeId() == SoPerspectiveCamera::getClassTypeId()) { float height = static_cast(cam)->heightAngle.getValue() / 2.0F; height = 2.0F * atan(tan(height) * scale); static_cast(cam)->heightAngle = height; } } std::vector ViewProviderMesh::getVisibleFacetsAfterZoom(const SbBox2s& rect, const SbViewportRegion& vp, SoCamera* camera) const { // camera copy will be deleted inside getVisibleFacets() // because the ref counter reaches 0 camera = static_cast(camera->copy()); boxZoom(rect, vp, camera); return getVisibleFacets(vp, camera); } void ViewProviderMesh::renderGLCallback(void* ud, SoAction* action) { if (action->isOfType(SoGLRenderAction::getClassTypeId())) { ViewProviderMesh* mesh = static_cast(ud); Gui::SoVisibleFaceAction fa; fa.apply(mesh->getRoot()); } } namespace MeshGui { class Vertex { public: Vertex(const MeshCore::MeshKernel& kernel, const MeshCore::MeshFacetGrid& grid, const Base::Vector3f& pos) : kernel(kernel) , grid(grid) , pos(pos) {} bool visible(const Base::Vector3f& base) const { MeshCore::MeshAlgorithm meshAlg(kernel); bool ok = meshAlg.IsVertexVisible(base, pos, grid); return ok; } private: const MeshCore::MeshKernel& kernel; const MeshCore::MeshFacetGrid& grid; Base::Vector3f pos; }; } // namespace MeshGui std::vector ViewProviderMesh::getVisibleFacets(const SbViewportRegion& vp, SoCamera* camera) const { const Mesh::PropertyMeshKernel& meshProp = static_cast(pcObject)->Mesh; const Mesh::MeshObject& mesh = meshProp.getValue(); uint32_t count = (uint32_t)mesh.countFacets(); SoSeparator* root = new SoSeparator; root->ref(); root->addChild(camera); SoLightModel* lm = new SoLightModel(); lm->model = SoLightModel::BASE_COLOR; root->addChild(lm); SoMaterial* mat = new SoMaterial(); mat->diffuseColor.setNum(count); SbColor* diffcol = mat->diffuseColor.startEditing(); for (uint32_t i = 0; i < count; i++) { float t {}; diffcol[i].setPackedValue(i << 8, t); } mat->diffuseColor.finishEditing(); SoMaterialBinding* bind = new SoMaterialBinding(); bind->value = SoMaterialBinding::PER_FACE; root->addChild(mat); root->addChild(bind); root->addChild(this->getCoordNode()); root->addChild(this->getShapeNode()); // Coin3d's off-screen renderer doesn't work out-of-the-box any more on most recent Linux // systems. So, use FreeCAD's offscreen renderer now. Gui::SoQtOffscreenRenderer renderer(vp); renderer.setBackgroundColor(SbColor4f(0.0F, 0.0F, 0.0F)); QImage img; renderer.render(root); renderer.writeToImage(img); root->unref(); int width = img.width(); int height = img.height(); QRgb color = 0; std::vector faces; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { QRgb rgb = img.pixel(x, y); rgb = rgb - (0xff << 24); if (rgb != 0 && rgb != color) { color = rgb; faces.push_back((Mesh::FacetIndex)rgb); } } } std::sort(faces.begin(), faces.end()); faces.erase(std::unique(faces.begin(), faces.end()), faces.end()); return faces; } void ViewProviderMesh::cutMesh(const std::vector& picked, const Base::ViewProjMethod& proj, SbBool inner) { // Get the facet indices inside the tool mesh std::vector indices; getFacetsFromPolygon(picked, proj, inner, indices); removeFacets(indices); } void ViewProviderMesh::trimMesh(const std::vector& polygon, const Base::ViewProjMethod& proj, SbBool inner) { Mesh::MeshObject* mesh = static_cast(pcObject)->Mesh.startEditing(); Base::Polygon2d polygon2d; for (auto it : polygon) { polygon2d.Add(Base::Vector2d(it[0], it[1])); } Mesh::MeshObject::CutType type = inner ? Mesh::MeshObject::INNER : Mesh::MeshObject::OUTER; mesh->trim(polygon2d, proj, type); static_cast(pcObject)->Mesh.finishEditing(); pcObject->purgeTouched(); } void ViewProviderMesh::splitMesh(const MeshCore::MeshKernel& toolMesh, const Base::Vector3f& normal, SbBool clip_inner) { // Get the attached mesh property Mesh::PropertyMeshKernel& meshProp = static_cast(pcObject)->Mesh; const MeshCore::MeshKernel& meshPropKernel = meshProp.getValue().getKernel(); // Get the facet indices inside the tool mesh std::vector indices; MeshCore::MeshFacetGrid cGrid(meshPropKernel); MeshCore::MeshAlgorithm cAlg(meshPropKernel); cAlg.GetFacetsFromToolMesh(toolMesh, normal, cGrid, indices); if (!clip_inner) { // get the indices that are completely outside std::vector complete(meshPropKernel.CountFacets()); std::generate(complete.begin(), complete.end(), Base::iotaGen(0)); std::sort(indices.begin(), indices.end()); std::vector complementary; std::back_insert_iterator> biit(complementary); std::set_difference(complete.begin(), complete.end(), indices.begin(), indices.end(), biit); indices = complementary; } // Remove the facets from the mesh and create a new one Mesh::MeshObject* kernel = meshProp.getValue().meshFromSegment(indices); removeFacets(indices); Mesh::Feature* splitMesh = static_cast( App::GetApplication().getActiveDocument()->addObject("Mesh::Feature", pcObject->getNameInDocument())); // Note: deletes also kernel splitMesh->Mesh.setValuePtr(kernel); static_cast(pcObject)->purgeTouched(); } void ViewProviderMesh::segmentMesh(const MeshCore::MeshKernel& toolMesh, const Base::Vector3f& normal, SbBool clip_inner) { // Get the attached mesh property Mesh::PropertyMeshKernel& meshProp = static_cast(pcObject)->Mesh; const MeshCore::MeshKernel& meshPropKernel = meshProp.getValue().getKernel(); // Get the facet indices inside the tool mesh std::vector indices; MeshCore::MeshFacetGrid cGrid(meshPropKernel); MeshCore::MeshAlgorithm cAlg(meshPropKernel); cAlg.GetFacetsFromToolMesh(toolMesh, normal, cGrid, indices); if (!clip_inner) { // get the indices that are completely outside std::vector complete(meshPropKernel.CountFacets()); std::generate(complete.begin(), complete.end(), Base::iotaGen(0)); std::sort(indices.begin(), indices.end()); std::vector complementary; std::back_insert_iterator> biit(complementary); std::set_difference(complete.begin(), complete.end(), indices.begin(), indices.end(), biit); indices = complementary; } Mesh::MeshObject* kernel = meshProp.startEditing(); kernel->addSegment(indices); meshProp.finishEditing(); static_cast(pcObject)->purgeTouched(); } void ViewProviderMesh::faceInfoCallback(void* ud, SoEventCallback* n) { const SoMouseButtonEvent* mbe = static_cast(n->getEvent()); Gui::View3DInventorViewer* view = static_cast(n->getUserData()); // Mark all incoming mouse button events as handled, especially, to deactivate the selection // node n->getAction()->setHandled(); if (mbe->getButton() == SoMouseButtonEvent::BUTTON2 && mbe->getState() == SoButtonEvent::UP) { n->setHandled(); // context-menu QMenu menu; QAction* cl = menu.addAction(QObject::tr("Leave info mode")); QAction* id = menu.exec(QCursor::pos()); if (cl == id) { view->setEditing(false); view->getWidget()->setCursor(QCursor(Qt::ArrowCursor)); view->removeEventCallback(SoMouseButtonEvent::getClassTypeId(), faceInfoCallback, ud); std::list glItems = view->getGraphicsItemsOfType(Gui::GLFlagWindow::getClassTypeId()); for (auto glItem : glItems) { view->removeGraphicsItem(glItem); delete glItem; } // See comment below ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/View"); hGrp->SetBool("ShowNaviCube", hGrp->GetBool("ShowNaviCube", true)); } } else if (mbe->getButton() == SoMouseButtonEvent::BUTTON1 && mbe->getState() == SoButtonEvent::DOWN) { const SoPickedPoint* point = n->getPickedPoint(); if (!point) { Base::Console().Message("No facet picked.\n"); return; } n->setHandled(); // By specifying the indexed mesh node 'pcFaceSet' we make sure that the picked point is // really from the mesh we render and not from any other geometry Gui::ViewProvider* vp = view->getViewProviderByPathFromTail(point->getPath()); if (!vp || !vp->isDerivedFrom()) { return; } // FIXME: The Flag class doesn't work well (flickering) when the NaviCube is enabled. // To avoid this the NaviCube is disabled for the time the flags are shown. // When leaving this mode the NaviCube can be displayed again. // For a proper solution it's best to move the Flag class to the QGraphicsView API. view->setEnabledNaviCube(false); ViewProviderMesh* that = static_cast(vp); const SoDetail* detail = point->getDetail(that->getShapeNode()); if (detail && detail->getTypeId() == SoFaceDetail::getClassTypeId()) { // get the boundary to the picked facet const SoFaceDetail* faceDetail = static_cast(detail); Mesh::FacetIndex uFacet = faceDetail->getFaceIndex(); that->faceInfo(uFacet); Gui::GLFlagWindow* flags = nullptr; std::list glItems = view->getGraphicsItemsOfType(Gui::GLFlagWindow::getClassTypeId()); if (glItems.empty()) { flags = new Gui::GLFlagWindow(view); view->addGraphicsItem(flags); } else { flags = static_cast(glItems.front()); } int point1 = static_cast(faceDetail->getPoint(0))->getCoordinateIndex(); int point2 = static_cast(faceDetail->getPoint(1))->getCoordinateIndex(); int point3 = static_cast(faceDetail->getPoint(2))->getCoordinateIndex(); Gui::Flag* flag = new Gui::Flag; flag->setText(QObject::tr("Index: %1").arg(uFacet)); QString toolTip = QString::fromLatin1("Facet index: %1\n" "Points: <%2, %3, %4>") .arg(uFacet) .arg(point1) .arg(point2) .arg(point3); flag->setToolTip(toolTip); flag->setOrigin(point->getPoint()); flags->addFlag(flag, Gui::FlagLayout::TopRight); } } } void ViewProviderMesh::fillHoleCallback(void* ud, SoEventCallback* n) { const SoMouseButtonEvent* mbe = static_cast(n->getEvent()); Gui::View3DInventorViewer* view = static_cast(n->getUserData()); // Mark all incoming mouse button events as handled, especially, to deactivate the selection // node n->getAction()->setHandled(); if (mbe->getButton() == SoMouseButtonEvent::BUTTON2 && mbe->getState() == SoButtonEvent::UP) { n->setHandled(); // context-menu QMenu menu; QAction* cl = menu.addAction(QObject::tr("Leave hole-filling mode")); QAction* id = menu.exec(QCursor::pos()); if (cl == id) { view->setEditing(false); view->setSelectionEnabled(true); view->getWidget()->setCursor(QCursor(Qt::ArrowCursor)); view->removeEventCallback(SoMouseButtonEvent::getClassTypeId(), fillHoleCallback, ud); } } else if (mbe->getButton() == SoMouseButtonEvent::BUTTON1 && mbe->getState() == SoButtonEvent::DOWN) { const SoPickedPoint* point = n->getPickedPoint(); if (!point) { Base::Console().Message("No facet picked.\n"); return; } n->setHandled(); // By specifying the indexed mesh node 'pcFaceSet' we make sure that the picked point is // really from the mesh we render and not from any other geometry Gui::ViewProvider* vp = view->getViewProviderByPathFromTail(point->getPath()); if (!vp || !vp->isDerivedFrom()) { return; } ViewProviderMesh* that = static_cast(vp); const SoDetail* detail = point->getDetail(that->getShapeNode()); if (detail && detail->getTypeId() == SoFaceDetail::getClassTypeId()) { // get the boundary to the picked facet Mesh::FacetIndex uFacet = ((SoFaceDetail*)detail)->getFaceIndex(); that->fillHole(uFacet); } } } void ViewProviderMesh::markPartCallback(void* ud, SoEventCallback* n) { // handle only mouse button events if (n->getEvent()->isOfType(SoMouseButtonEvent::getClassTypeId())) { const SoMouseButtonEvent* mbe = static_cast(n->getEvent()); Gui::View3DInventorViewer* view = static_cast(n->getUserData()); // Mark all incoming mouse button events as handled, especially, to deactivate the selection // node n->getAction()->setHandled(); if (mbe->getButton() == SoMouseButtonEvent::BUTTON2 && mbe->getState() == SoButtonEvent::UP) { n->setHandled(); // context-menu QMenu menu; QAction* cl = menu.addAction(QObject::tr("Leave removal mode")); QAction* rm = menu.addAction(QObject::tr("Delete selected faces")); QAction* cf = menu.addAction(QObject::tr("Clear selected faces")); QAction* id = menu.exec(QCursor::pos()); if (cl == id) { view->setEditing(false); view->setSelectionEnabled(true); view->removeEventCallback(SoMouseButtonEvent::getClassTypeId(), markPartCallback, ud); std::vector views = view->getViewProvidersOfType(ViewProviderMesh::getClassTypeId()); for (auto view : views) { static_cast(view)->clearSelection(); } } else if (cf == id) { std::vector views = view->getViewProvidersOfType(ViewProviderMesh::getClassTypeId()); for (auto view : views) { static_cast(view)->clearSelection(); } } else if (rm == id) { Gui::Application::Instance->activeDocument()->openCommand( QT_TRANSLATE_NOOP("Command", "Delete")); std::vector views = view->getViewProvidersOfType(ViewProviderMesh::getClassTypeId()); for (auto view : views) { static_cast(view)->deleteSelection(); } view->redraw(); Gui::Application::Instance->activeDocument()->commitCommand(); } } else if (mbe->getButton() == SoMouseButtonEvent::BUTTON1 && mbe->getState() == SoButtonEvent::DOWN) { const SoPickedPoint* point = n->getPickedPoint(); if (!point) { Base::Console().Message("No facet picked.\n"); return; } n->setHandled(); // By specifying the indexed mesh node 'pcFaceSet' we make sure that the picked point is // really from the mesh we render and not from any other geometry Gui::ViewProvider* vp = view->getViewProviderByPathFromTail(point->getPath()); if (!vp || !vp->isDerivedFrom()) { return; } ViewProviderMesh* that = static_cast(vp); const SoDetail* detail = point->getDetail(that->getShapeNode()); if (detail && detail->getTypeId() == SoFaceDetail::getClassTypeId()) { // get the boundary to the picked facet Mesh::FacetIndex uFacet = static_cast(detail)->getFaceIndex(); that->selectComponent(uFacet); } } } } void ViewProviderMesh::faceInfo(Mesh::FacetIndex uFacet) { Mesh::Feature* fea = static_cast(this->getObject()); const MeshCore::MeshKernel& rKernel = fea->Mesh.getValue().getKernel(); const MeshCore::MeshFacetArray& facets = rKernel.GetFacets(); if (uFacet < facets.size()) { MeshCore::MeshFacet face = facets[uFacet]; MeshCore::MeshGeomFacet tria = rKernel.GetFacet(face); Base::Console().Message( "Mesh: %s Facet %lu: Points: <%lu, %lu, %lu>, Neighbours: <%lu, %lu, %lu>\n" "Triangle: <[%.6f, %.6f, %.6f], [%.6f, %.6f, %.6f], [%.6f, %.6f, %.6f]>\n", fea->getNameInDocument(), uFacet, face._aulPoints[0], face._aulPoints[1], face._aulPoints[2], face._aulNeighbours[0], face._aulNeighbours[1], face._aulNeighbours[2], tria._aclPoints[0].x, tria._aclPoints[0].y, tria._aclPoints[0].z, tria._aclPoints[1].x, tria._aclPoints[1].y, tria._aclPoints[1].z, tria._aclPoints[2].x, tria._aclPoints[2].y, tria._aclPoints[2].z); } } void ViewProviderMesh::fillHole(Mesh::FacetIndex uFacet) { // get parameter from user settings Base::Reference hGrp = Gui::WindowParameter::getDefaultParameter()->GetGroup("Mod/Mesh"); int level = (int)hGrp->GetInt("FillHoleLevel", 2); // get the boundary to the picked facet std::list aBorder; Mesh::Feature* fea = static_cast(this->getObject()); const MeshCore::MeshKernel& rKernel = fea->Mesh.getValue().getKernel(); MeshCore::MeshRefPointToFacets cPt2Fac(rKernel); MeshCore::MeshAlgorithm meshAlg(rKernel); meshAlg.GetFacetBorder(uFacet, aBorder); std::vector boundary(aBorder.begin(), aBorder.end()); std::list> boundaries; boundaries.push_back(boundary); meshAlg.SplitBoundaryLoops(boundaries); std::vector newFacets; std::vector newPoints; unsigned long numberOfOldPoints = rKernel.CountPoints(); for (const auto& it : boundaries) { if (it.size() < 3 /* || it->size() > 200*/) { continue; } boundary = it; MeshCore::MeshFacetArray faces; MeshCore::MeshPointArray points; MeshCore::QuasiDelaunayTriangulator cTria /*(0.05f)*/; cTria.SetVerifier(new MeshCore::TriangulationVerifierV2); if (meshAlg.FillupHole(boundary, cTria, faces, points, level, &cPt2Fac)) { if (boundary.front() == boundary.back()) { boundary.pop_back(); } // the triangulation may produce additional points which we must take into account when // appending to the mesh unsigned long countBoundaryPoints = boundary.size(); unsigned long countDifference = points.size() - countBoundaryPoints; if (countDifference > 0) { MeshCore::MeshPointArray::_TIterator pt = points.begin() + countBoundaryPoints; for (unsigned long i = 0; i < countDifference; i++, pt++) { boundary.push_back(numberOfOldPoints++); newPoints.push_back(*pt); } } for (auto& face : faces) { face._aulPoints[0] = boundary[face._aulPoints[0]]; face._aulPoints[1] = boundary[face._aulPoints[1]]; face._aulPoints[2] = boundary[face._aulPoints[2]]; newFacets.push_back(face); } } } if (newFacets.empty()) { return; // nothing to do } // add the facets to the mesh and open a transaction object for the undo/redo stuff Gui::Application::Instance->activeDocument()->openCommand( QT_TRANSLATE_NOOP("Command", "Fill hole")); Mesh::MeshObject* kernel = fea->Mesh.startEditing(); kernel->addFacets(newFacets, newPoints, true); fea->Mesh.finishEditing(); Gui::Application::Instance->activeDocument()->commitCommand(); } void ViewProviderMesh::setFacetTransparency(const std::vector& facetTransparency) { if (pcShapeMaterial->diffuseColor.getNum() != int(facetTransparency.size())) { App::Color c = ShapeAppearance.getDiffuseColor(); pcShapeMaterial->diffuseColor.setNum(facetTransparency.size()); SbColor* cols = pcShapeMaterial->diffuseColor.startEditing(); for (std::size_t index = 0; index < facetTransparency.size(); ++index) { cols[index].setValue(c.r, c.g, c.b); } pcShapeMaterial->diffuseColor.finishEditing(); } pcShapeMaterial->transparency.setNum(facetTransparency.size()); float* tran = pcShapeMaterial->transparency.startEditing(); for (std::size_t index = 0; index < facetTransparency.size(); ++index) { tran[index] = facetTransparency[index]; } pcShapeMaterial->transparency.finishEditing(); pcMatBinding->value = SoMaterialBinding::PER_FACE; } void ViewProviderMesh::resetFacetTransparency() { pcMatBinding->value = SoMaterialBinding::OVERALL; App::Color c = ShapeAppearance.getDiffuseColor(); pcShapeMaterial->diffuseColor.setValue(c.r, c.g, c.b); pcShapeMaterial->transparency.setValue(0); } /*! The triangles with the passed indices are already added to the mesh. */ void ViewProviderMesh::appendFacets(const std::vector&) {} void ViewProviderMesh::removeFacets(const std::vector& facets) { // Get the attached mesh property Mesh::PropertyMeshKernel& meshProp = static_cast(pcObject)->Mesh; Mesh::MeshObject* kernel = meshProp.startEditing(); // get the colour property if there App::PropertyColorList* prop = getColorProperty(); bool ok = Coloring.getValue(); if (prop && prop->getSize() == static_cast(kernel->countPoints())) { std::vector pointDegree; unsigned long invalid = kernel->getPointDegree(facets, pointDegree); if (invalid > 0) { // switch off coloring mode Coloring.setValue(false); const std::vector& colors = prop->getValues(); std::vector valid_colors; valid_colors.reserve(kernel->countPoints() - invalid); std::size_t numPoints = pointDegree.size(); for (std::size_t index = 0; index < numPoints; index++) { if (pointDegree[index] > 0) { valid_colors.push_back(colors[index]); } } prop->setValues(valid_colors); } } else if (prop && prop->getSize() == static_cast(kernel->countFacets())) { // switch off coloring mode Coloring.setValue(false); std::vector validFacets(kernel->countFacets(), true); for (auto it : facets) { validFacets[it] = false; } const std::vector& colors = prop->getValues(); std::vector valid_colors; valid_colors.reserve(colors.size()); std::size_t numColors = colors.size(); for (std::size_t index = 0; index < numColors; index++) { if (validFacets[index]) { valid_colors.push_back(colors[index]); } } prop->setValues(valid_colors); } // Remove the facets from the mesh and open a transaction object for the undo/redo stuff kernel->deleteFacets(facets); meshProp.finishEditing(); pcObject->purgeTouched(); Coloring.setValue(ok); } void ViewProviderMesh::selectFacet(Mesh::FacetIndex facet) { std::vector selection; selection.push_back(facet); const Mesh::MeshObject& rMesh = static_cast(pcObject)->Mesh.getValue(); rMesh.addFacetsToSelection(selection); // Colorize the selection pcMatBinding->value = SoMaterialBinding::PER_FACE; int uCtFacets = (int)rMesh.countFacets(); if (uCtFacets != pcShapeMaterial->diffuseColor.getNum()) { highlightSelection(); } else { pcShapeMaterial->diffuseColor.set1Value(facet, 1.0F, 0.0F, 0.0F); } } void ViewProviderMesh::deselectFacet(Mesh::FacetIndex facet) { std::vector selection; selection.push_back(facet); const Mesh::MeshObject& rMesh = static_cast(pcObject)->Mesh.getValue(); rMesh.removeFacetsFromSelection(selection); // Colorize the selection pcMatBinding->value = SoMaterialBinding::PER_FACE; int uCtFacets = (int)rMesh.countFacets(); if (rMesh.hasSelectedFacets()) { if (uCtFacets != pcShapeMaterial->diffuseColor.getNum()) { highlightSelection(); } else { App::Color c = ShapeAppearance.getDiffuseColor(); pcShapeMaterial->diffuseColor.set1Value(facet, c.r, c.g, c.b); } } else { unhighlightSelection(); } } bool ViewProviderMesh::isFacetSelected(Mesh::FacetIndex facet) { const Mesh::MeshObject& rMesh = static_cast(pcObject)->Mesh.getValue(); const MeshCore::MeshFacetArray& faces = rMesh.getKernel().GetFacets(); return faces[facet].IsFlag(MeshCore::MeshFacet::SELECTED); } void ViewProviderMesh::selectComponent(Mesh::FacetIndex uFacet) { std::vector selection; selection.push_back(uFacet); MeshCore::MeshTopFacetVisitor clVisitor(selection); const Mesh::MeshObject& rMesh = static_cast(pcObject)->Mesh.getValue(); const MeshCore::MeshKernel& rKernel = rMesh.getKernel(); MeshCore::MeshAlgorithm(rKernel).ResetFacetFlag(MeshCore::MeshFacet::VISIT); rKernel.VisitNeighbourFacets(clVisitor, uFacet); rMesh.addFacetsToSelection(selection); // Colorize the selection highlightSelection(); } void ViewProviderMesh::deselectComponent(Mesh::FacetIndex uFacet) { std::vector selection; selection.push_back(uFacet); MeshCore::MeshTopFacetVisitor clVisitor(selection); const Mesh::MeshObject& rMesh = static_cast(pcObject)->Mesh.getValue(); const MeshCore::MeshKernel& rKernel = rMesh.getKernel(); MeshCore::MeshAlgorithm(rKernel).ResetFacetFlag(MeshCore::MeshFacet::VISIT); rKernel.VisitNeighbourFacets(clVisitor, uFacet); rMesh.removeFacetsFromSelection(selection); // Colorize the selection if (rMesh.hasSelectedFacets()) { highlightSelection(); } else { unhighlightSelection(); } } void ViewProviderMesh::setSelection(const std::vector& indices) { const Mesh::MeshObject& rMesh = static_cast(pcObject)->Mesh.getValue(); rMesh.clearFacetSelection(); rMesh.addFacetsToSelection(indices); // Colorize the selection if (indices.empty()) { unhighlightSelection(); } else { highlightSelection(); } } void ViewProviderMesh::addSelection(const std::vector& indices) { const Mesh::MeshObject& rMesh = static_cast(pcObject)->Mesh.getValue(); rMesh.addFacetsToSelection(indices); // Colorize the selection highlightSelection(); } void ViewProviderMesh::removeSelection(const std::vector& indices) { const Mesh::MeshObject& rMesh = static_cast(pcObject)->Mesh.getValue(); rMesh.removeFacetsFromSelection(indices); // Colorize the selection if (rMesh.hasSelectedFacets()) { highlightSelection(); } else { unhighlightSelection(); } } void ViewProviderMesh::invertSelection() { const Mesh::MeshObject& rMesh = static_cast(pcObject)->Mesh.getValue(); const MeshCore::MeshFacetArray& faces = rMesh.getKernel().GetFacets(); MeshCore::MeshIsNotFlag flag; unsigned long num_notsel = std::count_if(faces.begin(), faces.end(), [flag](const MeshCore::MeshFacet& f) { return flag(f, MeshCore::MeshFacet::SELECTED); }); std::vector notselect; notselect.reserve(num_notsel); MeshCore::MeshFacetArray::_TConstIterator beg = faces.begin(); MeshCore::MeshFacetArray::_TConstIterator end = faces.end(); for (MeshCore::MeshFacetArray::_TConstIterator jt = beg; jt != end; ++jt) { if (!jt->IsFlag(MeshCore::MeshFacet::SELECTED)) { notselect.push_back(jt - beg); } } setSelection(notselect); } void ViewProviderMesh::clearSelection() { const Mesh::MeshObject& rMesh = static_cast(pcObject)->Mesh.getValue(); rMesh.clearFacetSelection(); unhighlightSelection(); } void ViewProviderMesh::deleteSelection() { std::vector indices; Mesh::PropertyMeshKernel& meshProp = static_cast(pcObject)->Mesh; const Mesh::MeshObject& rMesh = meshProp.getValue(); rMesh.getFacetsFromSelection(indices); if (!indices.empty()) { rMesh.clearFacetSelection(); unhighlightSelection(); removeFacets(indices); } } bool ViewProviderMesh::hasSelection() const { Mesh::PropertyMeshKernel& meshProp = static_cast(pcObject)->Mesh; const Mesh::MeshObject& rMesh = meshProp.getValue(); return rMesh.hasSelectedFacets(); } void ViewProviderMesh::selectArea(short x, short y, short w, short h, const SbViewportRegion& region, SoCamera* camera) { SbViewportRegion vp; vp.setViewportPixels(x, y, w, h); std::vector faces = getFacetsOfRegion(vp, region, camera); const Mesh::MeshObject& rMesh = static_cast(pcObject)->Mesh.getValue(); rMesh.addFacetsToSelection(faces); // Colorize the selected part highlightSelection(); } void ViewProviderMesh::highlightSelection() { std::vector selection; const Mesh::MeshObject& rMesh = static_cast(pcObject)->Mesh.getValue(); rMesh.getFacetsFromSelection(selection); if (selection.empty()) { // If no faces are selected then simply return even // without calling unhighlightSelection() return; } // Colorize the selection pcMatBinding->value = SoMaterialBinding::PER_FACE; App::Color c = ShapeAppearance.getDiffuseColor(); int uCtFacets = (int)rMesh.countFacets(); pcShapeMaterial->diffuseColor.setNum(uCtFacets); SbColor* cols = pcShapeMaterial->diffuseColor.startEditing(); for (int i = 0; i < uCtFacets; i++) { cols[i].setValue(c.r, c.g, c.b); } for (Mesh::FacetIndex it : selection) { cols[it].setValue(1.0F, 0.0F, 0.0F); } pcShapeMaterial->diffuseColor.finishEditing(); } void ViewProviderMesh::unhighlightSelection() { App::Color c = ShapeAppearance.getDiffuseColor(); pcMatBinding->value = SoMaterialBinding::OVERALL; pcShapeMaterial->diffuseColor.setNum(1); pcShapeMaterial->diffuseColor.setValue(c.r, c.g, c.b); } void ViewProviderMesh::setHighlightedComponents(bool on) { if (on) { highlightMode = HighlighMode::Component; highlightComponents(); } else { highlightMode = HighlighMode::None; unhighlightSelection(); } } void ViewProviderMesh::highlightComponents() { const Mesh::MeshObject& rMesh = static_cast(pcObject)->Mesh.getValue(); std::vector> comps = rMesh.getComponents(); // Colorize the components pcMatBinding->value = SoMaterialBinding::PER_FACE; int uCtFacets = (int)rMesh.countFacets(); pcShapeMaterial->diffuseColor.setNum(uCtFacets); SbColor* cols = pcShapeMaterial->diffuseColor.startEditing(); for (const auto& comp : comps) { float fMax = (float)RAND_MAX; float fRed = (float)rand() / fMax; float fGrn = (float)rand() / fMax; float fBlu = (float)rand() / fMax; for (Mesh::FacetIndex jt : comp) { cols[jt].setValue(fRed, fGrn, fBlu); } } pcShapeMaterial->diffuseColor.finishEditing(); } void ViewProviderMesh::setHighlightedSegments(bool on) { if (on) { highlightMode = HighlighMode::Segment; highlightSegments(); } else { highlightMode = HighlighMode::None; unhighlightSelection(); } } void ViewProviderMesh::highlightSegments() { std::vector colors; const Mesh::MeshObject& rMesh = static_cast(pcObject)->Mesh.getValue(); unsigned long numSegm = rMesh.countSegments(); colors.resize(numSegm, this->ShapeAppearance.getDiffuseColor()); for (unsigned long i = 0; i < numSegm; i++) { App::Color col; if (col.fromHexString(rMesh.getSegment(i).getColor())) { colors[i] = col; } } highlightSegments(colors); } void ViewProviderMesh::highlightSegments(const std::vector& colors) { const Mesh::MeshObject& rMesh = static_cast(pcObject)->Mesh.getValue(); unsigned long numSegm = rMesh.countSegments(); if (numSegm > 0 && numSegm == colors.size()) { // Colorize the components pcMatBinding->value = SoMaterialBinding::PER_FACE; int uCtFacets = (int)rMesh.countFacets(); pcShapeMaterial->diffuseColor.setNum(uCtFacets); SbColor* cols = pcShapeMaterial->diffuseColor.startEditing(); for (unsigned long i = 0; i < numSegm; i++) { std::vector segm = rMesh.getSegment(i).getIndices(); float fRed = colors[i].r; float fGrn = colors[i].g; float fBlu = colors[i].b; for (Mesh::FacetIndex it : segm) { cols[it].setValue(fRed, fGrn, fBlu); } } pcShapeMaterial->diffuseColor.finishEditing(); } else if (colors.size() == 1) { pcMatBinding->value = SoMaterialBinding::OVERALL; float fRed = colors[0].r; float fGrn = colors[0].g; float fBlu = colors[0].b; pcShapeMaterial->diffuseColor.setValue(fRed, fGrn, fBlu); } } void ViewProviderMesh::setHighlightedColors(bool on) { if (on) { highlightMode = HighlighMode::Color; highlightColors(); } else { highlightMode = HighlighMode::None; unhighlightSelection(); } } void ViewProviderMesh::highlightColors() { const Mesh::MeshObject& rMesh = static_cast(pcObject)->Mesh.getValue(); { App::PropertyColorList* prop = Base::freecad_dynamic_cast( pcObject->getPropertyByName("FaceColors")); if (prop && prop->getSize() == int(rMesh.countFacets())) { setColorPerFace(prop); } } { App::PropertyColorList* prop = Base::freecad_dynamic_cast( pcObject->getPropertyByName("VertexColors")); if (prop && prop->getSize() == int(rMesh.countPoints())) { setColorPerVertex(prop); } } } bool ViewProviderMesh::canHighlightColors() const { const Mesh::MeshObject& rMesh = static_cast(pcObject)->Mesh.getValue(); { App::PropertyColorList* prop = Base::freecad_dynamic_cast( pcObject->getPropertyByName("FaceColors")); if (prop && prop->getSize() == int(rMesh.countFacets())) { return true; } } { App::PropertyColorList* prop = Base::freecad_dynamic_cast( pcObject->getPropertyByName("VertexColors")); if (prop && prop->getSize() == int(rMesh.countPoints())) { return true; } } return false; } PyObject* ViewProviderMesh::getPyObject() { if (!pyViewObject) { pyViewObject = new ViewProviderMeshPy(this); } pyViewObject->IncRef(); return pyViewObject; } // ------------------------------------------------------ PROPERTY_SOURCE(MeshGui::ViewProviderIndexedFaceSet, MeshGui::ViewProviderMesh) ViewProviderIndexedFaceSet::ViewProviderIndexedFaceSet() { // NOLINTBEGIN pcMeshCoord = nullptr; pcMeshFaces = nullptr; // NOLINTEND } ViewProviderIndexedFaceSet::~ViewProviderIndexedFaceSet() = default; /** * Extracts the mesh data from the feature \a pcFeature and creates * an Inventor node \a SoNode with these data. */ void ViewProviderIndexedFaceSet::attach(App::DocumentObject* pcFeat) { ViewProviderMesh::attach(pcFeat); pcMeshCoord = new SoCoordinate3; pcHighlight->addChild(pcMeshCoord); pcMeshFaces = new SoFCIndexedFaceSet; pcHighlight->addChild(pcMeshFaces); // read the threshold from the preferences Base::Reference hGrp = Gui::WindowParameter::getDefaultParameter()->GetGroup("Mod/Mesh"); int size = hGrp->GetInt("RenderTriangleLimit", -1); if (size > 0) { static_cast(pcMeshFaces)->renderTriangleLimit = (unsigned int)(pow(10.0F, size)); } } void ViewProviderIndexedFaceSet::updateData(const App::Property* prop) { ViewProviderMesh::updateData(prop); if (prop->is()) { ViewProviderMeshBuilder builder; builder.createMesh(prop, pcMeshCoord, pcMeshFaces); showOpenEdges(OpenEdges.getValue()); highlightSelection(); } } void ViewProviderIndexedFaceSet::showOpenEdges(bool show) { if (pcOpenEdge) { // remove the node and destroy the data pcRoot->removeChild(pcOpenEdge); pcOpenEdge = nullptr; } if (show) { pcOpenEdge = new SoSeparator(); pcOpenEdge->addChild(pcLineStyle); pcOpenEdge->addChild(pOpenColor); pcOpenEdge->addChild(pcMeshCoord); SoIndexedLineSet* lines = new SoIndexedLineSet; pcOpenEdge->addChild(lines); // add to the highlight node pcRoot->addChild(pcOpenEdge); // Build up the lines with indices to the list of vertices 'pcMeshCoord' int index = 0; const MeshCore::MeshKernel& rMesh = static_cast(pcObject)->Mesh.getValue().getKernel(); const MeshCore::MeshFacetArray& rFaces = rMesh.GetFacets(); for (const auto& rFace : rFaces) { for (int i = 0; i < 3; i++) { if (rFace._aulNeighbours[i] == MeshCore::FACET_INDEX_MAX) { lines->coordIndex.set1Value(index++, rFace._aulPoints[i]); lines->coordIndex.set1Value(index++, rFace._aulPoints[(i + 1) % 3]); lines->coordIndex.set1Value(index++, SO_END_LINE_INDEX); } } } } } SoShape* ViewProviderIndexedFaceSet::getShapeNode() const { return this->pcMeshFaces; } SoNode* ViewProviderIndexedFaceSet::getCoordNode() const { return this->pcMeshCoord; } // ------------------------------------------------------ PROPERTY_SOURCE(MeshGui::ViewProviderMeshObject, MeshGui::ViewProviderMesh) ViewProviderMeshObject::ViewProviderMeshObject() { // NOLINTBEGIN pcMeshNode = nullptr; pcMeshShape = nullptr; // NOLINTEND } ViewProviderMeshObject::~ViewProviderMeshObject() = default; void ViewProviderMeshObject::attach(App::DocumentObject* pcFeat) { ViewProviderMesh::attach(pcFeat); pcMeshNode = new SoFCMeshObjectNode; pcHighlight->addChild(pcMeshNode); pcMeshShape = new SoFCMeshObjectShape; pcHighlight->addChild(pcMeshShape); // read the threshold from the preferences Base::Reference hGrp = Gui::WindowParameter::getDefaultParameter()->GetGroup("Mod/Mesh"); int size = hGrp->GetInt("RenderTriangleLimit", -1); if (size > 0) { pcMeshShape->renderTriangleLimit = (unsigned int)(pow(10.0F, size)); } } void ViewProviderMeshObject::updateData(const App::Property* prop) { ViewProviderMesh::updateData(prop); if (prop->is()) { const Mesh::PropertyMeshKernel* mesh = static_cast(prop); this->pcMeshNode->mesh.setValue( Base::Reference(mesh->getValuePtr())); // Needs to update internal bounding box caches this->pcMeshShape->touch(); } } void ViewProviderMeshObject::showOpenEdges(bool show) { if (pcOpenEdge) { // remove the node and destroy the data pcRoot->removeChild(pcOpenEdge); pcOpenEdge = nullptr; } if (show) { pcOpenEdge = new SoSeparator(); pcOpenEdge->addChild(pcLineStyle); pcOpenEdge->addChild(pOpenColor); pcOpenEdge->addChild(pcMeshNode); pcOpenEdge->addChild(new SoFCMeshObjectBoundary); // add to the highlight node pcRoot->addChild(pcOpenEdge); } } SoShape* ViewProviderMeshObject::getShapeNode() const { return this->pcMeshShape; } SoNode* ViewProviderMeshObject::getCoordNode() const { return this->pcMeshNode; }