// SPDX-License-Identifier: LGPL-2.1-or-later /*************************************************************************** * Copyright (c) 2011 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 #ifdef FC_OS_WIN32 # include #endif #ifdef FC_OS_MACOSX # include #else # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "ViewProviderExt.h" #include "SoBrepPointSet.h" using namespace PartGui; SO_NODE_SOURCE(SoBrepPointSet) void SoBrepPointSet::initClass() { SO_NODE_INIT_CLASS(SoBrepPointSet, SoPointSet, "PointSet"); } SoBrepPointSet::SoBrepPointSet() : selContext(std::make_shared()) , selContext2(std::make_shared()) { SO_NODE_CONSTRUCTOR(SoBrepPointSet); } void SoBrepPointSet::GLRender(SoGLRenderAction* action) { auto state = action->getState(); selCounter.checkRenderCache(state); const SoCoordinateElement* coords = SoCoordinateElement::getInstance(state); int num = coords->getNum() - this->startIndex.getValue(); if (num < 0) { // Fixes: #0000545: Undo revolve causes crash 'illegal storage' return; } SelContextPtr ctx2; SelContextPtr ctx = Gui::SoFCSelectionRoot::getRenderContext(this, selContext, ctx2); if (ctx2 && ctx2->selectionIndex.empty()) { return; } if (selContext2->checkGlobal(ctx)) { ctx = selContext2; } bool hasContextHighlight = ctx && ctx->isHighlighted() && !ctx->isHighlightAll() && ctx->highlightIndex >= 0; // for clarifyselection, add this node to delayed path if it is highlighted and render it on // top of everything else (highest priority) if (Gui::Selection().isClarifySelectionActive() && hasContextHighlight && !Gui::SoDelayedAnnotationsElement::isProcessingDelayedPaths) { if (viewProvider) { viewProvider->setFaceHighlightActive(true); } Gui::SoDelayedAnnotationsElement::addDelayedPath( action->getState(), action->getCurPath()->copy(), 300 ); return; } if (ctx && ctx->highlightIndex == std::numeric_limits::max()) { if (ctx->selectionIndex.empty() || ctx->isSelectAll()) { if (ctx2) { ctx2->selectionColor = ctx->highlightColor; renderSelection(action, ctx2); } else { renderHighlight(action, ctx); } } else { if (!action->isRenderingDelayedPaths()) { renderSelection(action, ctx); } if (ctx2) { ctx2->selectionColor = ctx->highlightColor; renderSelection(action, ctx2); } else { renderHighlight(action, ctx); } if (action->isRenderingDelayedPaths()) { renderSelection(action, ctx); } } return; } if (!action->isRenderingDelayedPaths()) { renderHighlight(action, ctx); } if (ctx && !ctx->selectionIndex.empty()) { if (ctx->isSelectAll()) { if (ctx2 && !ctx2->selectionIndex.empty()) { ctx2->selectionColor = ctx->selectionColor; renderSelection(action, ctx2); } else { renderSelection(action, ctx); } if (action->isRenderingDelayedPaths()) { renderHighlight(action, ctx); } return; } if (!action->isRenderingDelayedPaths()) { renderSelection(action, ctx); } } if (ctx2 && !ctx2->selectionIndex.empty()) { renderSelection(action, ctx2, false); } else if (Gui::SoDelayedAnnotationsElement::isProcessingDelayedPaths) { glPushAttrib(GL_DEPTH_BUFFER_BIT); glDepthFunc(GL_ALWAYS); inherited::GLRender(action); glPopAttrib(); } else { inherited::GLRender(action); } // Workaround for #0000433 // #if !defined(FC_OS_WIN32) if (!action->isRenderingDelayedPaths()) { renderHighlight(action, ctx); } if (ctx && !ctx->selectionIndex.empty()) { renderSelection(action, ctx); } if (action->isRenderingDelayedPaths()) { renderHighlight(action, ctx); } // #endif } void SoBrepPointSet::GLRenderBelowPath(SoGLRenderAction* action) { inherited::GLRenderBelowPath(action); } void SoBrepPointSet::getBoundingBox(SoGetBoundingBoxAction* action) { SelContextPtr ctx2 = Gui::SoFCSelectionRoot::getSecondaryActionContext(action, this); if (!ctx2 || ctx2->isSelectAll()) { inherited::getBoundingBox(action); return; } if (ctx2->selectionIndex.empty()) { return; } auto state = action->getState(); auto coords = SoCoordinateElement::getInstance(state); const SbVec3f* coords3d = coords->getArrayPtr3(); int numverts = coords->getNum(); int startIndex = this->startIndex.getValue(); SbBox3f bbox; for (auto idx : ctx2->selectionIndex) { if (idx >= startIndex && idx < numverts) { bbox.extendBy(coords3d[idx]); } } if (!bbox.isEmpty()) { action->extendBy(bbox); } } void SoBrepPointSet::renderHighlight(SoGLRenderAction* action, SelContextPtr ctx) { if (!ctx || ctx->highlightIndex < 0) { return; } SoState* state = action->getState(); state->push(); float ps = SoPointSizeElement::get(state); if (ps < 4.0f) { SoPointSizeElement::set(state, this, 4.0f); } SoLazyElement::setEmissive(state, &ctx->highlightColor); packedColor = ctx->highlightColor.getPackedValue(0.0); SoLazyElement::setPacked(state, this, 1, &packedColor, false); const SoCoordinateElement* coords; const SbVec3f* normals; this->getVertexData(state, coords, normals, false); SoMaterialBundle mb(action); mb.sendFirst(); // make sure we have the correct material int id = ctx->highlightIndex; const SbVec3f* coords3d = coords->getArrayPtr3(); if (coords3d) { if (id == std::numeric_limits::max()) { glBegin(GL_POINTS); for (int idx = startIndex.getValue(); idx < coords->getNum(); ++idx) { glVertex3fv((const GLfloat*)(coords3d + idx)); } glEnd(); } else if (id < this->startIndex.getValue() || id >= coords->getNum()) { SoDebugError::postWarning("SoBrepPointSet::renderHighlight", "highlightIndex out of range"); } else { glBegin(GL_POINTS); glVertex3fv((const GLfloat*)(coords3d + id)); glEnd(); } } state->pop(); } void SoBrepPointSet::renderSelection(SoGLRenderAction* action, SelContextPtr ctx, bool push) { SoState* state = action->getState(); if (push) { state->push(); float ps = SoPointSizeElement::get(state); if (ps < 4.0f) { SoPointSizeElement::set(state, this, 4.0f); } SoLazyElement::setEmissive(state, &ctx->selectionColor); packedColor = ctx->selectionColor.getPackedValue(0.0); SoLazyElement::setPacked(state, this, 1, &packedColor, false); } const SoCoordinateElement* coords; const SbVec3f* normals; this->getVertexData(state, coords, normals, false); SoMaterialBundle mb(action); mb.sendFirst(); // make sure we have the correct material bool warn = false; int startIndex = this->startIndex.getValue(); const SbVec3f* coords3d = coords->getArrayPtr3(); if (coords3d) { glBegin(GL_POINTS); if (ctx->isSelectAll()) { for (int idx = startIndex; idx < coords->getNum(); ++idx) { glVertex3fv((const GLfloat*)(coords3d + idx)); } } else { for (auto idx : ctx->selectionIndex) { if (idx >= startIndex && idx < coords->getNum()) { glVertex3fv((const GLfloat*)(coords3d + idx)); } else { warn = true; } } } glEnd(); } if (warn) { SoDebugError::postWarning("SoBrepPointSet::renderSelection", "selectionIndex out of range"); } if (push) { state->pop(); } } void SoBrepPointSet::doAction(SoAction* action) { if (action->getTypeId() == Gui::SoHighlightElementAction::getClassTypeId()) { Gui::SoHighlightElementAction* hlaction = static_cast(action); selCounter.checkAction(hlaction); if (!hlaction->isHighlighted()) { SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action, this, selContext, false); if (ctx) { ctx->highlightIndex = -1; touch(); } return; } SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action, this, selContext); const SoDetail* detail = hlaction->getElement(); if (!detail) { ctx->highlightIndex = std::numeric_limits::max(); ctx->highlightColor = hlaction->getColor(); touch(); return; } else if (!detail->isOfType(SoPointDetail::getClassTypeId())) { ctx->highlightIndex = -1; touch(); return; } int index = static_cast(detail)->getCoordinateIndex(); if (index != ctx->highlightIndex) { ctx->highlightIndex = index; ctx->highlightColor = hlaction->getColor(); touch(); } return; } else if (action->getTypeId() == Gui::SoSelectionElementAction::getClassTypeId()) { Gui::SoSelectionElementAction* selaction = static_cast(action); switch (selaction->getType()) { case Gui::SoSelectionElementAction::All: { SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action, this, selContext); selCounter.checkAction(selaction, ctx); ctx->selectionColor = selaction->getColor(); ctx->selectionIndex.clear(); ctx->selectionIndex.insert(-1); touch(); return; } case Gui::SoSelectionElementAction::None: if (selaction->isSecondary()) { if (Gui::SoFCSelectionRoot::removeActionContext(action, this)) { touch(); } } else { SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action, this, selContext, false); if (ctx) { ctx->selectionIndex.clear(); touch(); } } return; case Gui::SoSelectionElementAction::Remove: case Gui::SoSelectionElementAction::Append: { const SoDetail* detail = selaction->getElement(); if (!detail || !detail->isOfType(SoPointDetail::getClassTypeId())) { if (selaction->isSecondary()) { // For secondary context, a detail of different type means // the user may want to partial render only other type of // geometry. So we call below to obtain a action context. // If no secondary context exist, it will create an empty // one, and an empty secondary context inhibites drawing // here. auto ctx = Gui::SoFCSelectionRoot::getActionContext(action, this, selContext); selCounter.checkAction(selaction, ctx); touch(); } return; } int index = static_cast(detail)->getCoordinateIndex(); if (selaction->getType() == Gui::SoSelectionElementAction::Append) { SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action, this, selContext); selCounter.checkAction(selaction, ctx); ctx->selectionColor = selaction->getColor(); if (ctx->isSelectAll()) { ctx->selectionIndex.clear(); } if (ctx->selectionIndex.insert(index).second) { touch(); } } else { SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action, this, selContext, false); if (ctx && ctx->removeIndex(index)) { touch(); } } break; } default: break; } return; } inherited::doAction(action); }