/*************************************************************************** * 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 "PreCompiled.h" #ifndef _PreComp_ # 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 #endif #include #include "SoBrepEdgeSet.h" using namespace PartGui; SO_NODE_SOURCE(SoBrepEdgeSet) struct SoBrepEdgeSet::SelContext: Gui::SoFCSelectionContext { std::vector hl, sl; }; void SoBrepEdgeSet::initClass() { SO_NODE_INIT_CLASS(SoBrepEdgeSet, SoIndexedLineSet, "IndexedLineSet"); } SoBrepEdgeSet::SoBrepEdgeSet() : selContext(std::make_shared()) , selContext2(std::make_shared()) { SO_NODE_CONSTRUCTOR(SoBrepEdgeSet); } void SoBrepEdgeSet::GLRender(SoGLRenderAction *action) { auto state = action->getState(); selCounter.checkRenderCache(state); SelContextPtr ctx2; SelContextPtr ctx = Gui::SoFCSelectionRoot::getRenderContext(this,selContext,ctx2); if(ctx2 && ctx2->selectionIndex.empty()) return; if(selContext2->checkGlobal(ctx)) { if(selContext2->isSelectAll()) { selContext2->sl.clear(); selContext2->sl.push_back(-1); }else if(ctx) selContext2->sl = ctx->sl; if(selContext2->highlightIndex==INT_MAX) { selContext2->hl.clear(); selContext2->hl.push_back(-1); }else if(ctx) selContext2->hl = ctx->hl; ctx = selContext2; } if(ctx && ctx->highlightIndex==INT_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->selectionColor = ctx->selectionColor; renderSelection(action,ctx2); }else if(ctx->isSelectAll()) 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 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 SoBrepEdgeSet::GLRenderBelowPath(SoGLRenderAction * action) { inherited::GLRenderBelowPath(action); } void SoBrepEdgeSet::getBoundingBox(SoGetBoundingBoxAction * action) { SelContextPtr ctx2 = Gui::SoFCSelectionRoot::getSecondaryActionContext(action,this); if(!ctx2 || (ctx2->sl.size()==1 && ctx2->sl[0]<0)) { inherited::getBoundingBox(action); return; } if(ctx2->sl.empty()) return; auto state = action->getState(); auto coords = SoCoordinateElement::getInstance(state); const SbVec3f *coords3d = coords->getArrayPtr3(); if(!validIndexes(coords,ctx2->sl)) return; SbBox3f bbox; int32_t i; const int32_t *cindices = &ctx2->sl[0]; const int32_t *end = cindices + ctx2->sl.size(); while (cindices < end) { bbox.extendBy(coords3d[*cindices++]); i = (cindices < end) ? *cindices++ : -1; while (i >= 0) { bbox.extendBy(coords3d[i]); i = cindices < end ? *cindices++ : -1; } } if(!bbox.isEmpty()) action->extendBy(bbox); } void SoBrepEdgeSet::renderShape(const SoGLCoordinateElement * const coords, const int32_t *cindices, int numindices) { const SbVec3f * coords3d = coords->getArrayPtr3(); int32_t i; int previ; const int32_t *end = cindices + numindices; while (cindices < end) { glBegin(GL_LINE_STRIP); previ = *cindices++; i = (cindices < end) ? *cindices++ : -1; while (i >= 0) { glVertex3fv((const GLfloat*) (coords3d + previ)); glVertex3fv((const GLfloat*) (coords3d + i)); previ = i; i = cindices < end ? *cindices++ : -1; } glEnd(); } } void SoBrepEdgeSet::renderHighlight(SoGLRenderAction *action, SelContextPtr ctx) { if(!ctx || ctx->highlightIndex<0) return; SoState * state = action->getState(); state->push(); //SoLineWidthElement::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; const int32_t * cindices; int numcindices; const int32_t * nindices; const int32_t * tindices; const int32_t * mindices; SbBool normalCacheUsed; this->getVertexData(state, coords, normals, cindices, nindices, tindices, mindices, numcindices, false, normalCacheUsed); SoMaterialBundle mb(action); mb.sendFirst(); // make sure we have the correct material int num = (int)ctx->hl.size(); if (num > 0) { if (ctx->hl[0] < 0) { renderShape(static_cast(coords), cindices, numcindices); } else { const int32_t* id = &(ctx->hl[0]); if (!validIndexes(coords, ctx->hl)) { SoDebugError::postWarning("SoBrepEdgeSet::renderHighlight", "highlightIndex out of range"); } else { renderShape(static_cast(coords), id, num); } } } state->pop(); } void SoBrepEdgeSet::renderSelection(SoGLRenderAction *action, SelContextPtr ctx, bool push) { SoState * state = action->getState(); if(push){ state->push(); //SoLineWidthElement::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; const int32_t * cindices; int numcindices; const int32_t * nindices; const int32_t * tindices; const int32_t * mindices; SbBool normalCacheUsed; this->getVertexData(state, coords, normals, cindices, nindices, tindices, mindices, numcindices, false, normalCacheUsed); SoMaterialBundle mb(action); mb.sendFirst(); // make sure we have the correct material int num = (int)ctx->sl.size(); if (num > 0) { if (ctx->sl[0] < 0) { renderShape(static_cast(coords), cindices, numcindices); } else { cindices = &(ctx->sl[0]); numcindices = (int)ctx->sl.size(); if (!validIndexes(coords, ctx->sl)) { SoDebugError::postWarning("SoBrepEdgeSet::renderSelection", "selectionIndex out of range"); } else { renderShape(static_cast(coords), cindices, numcindices); } } } if(push) state->pop(); } bool SoBrepEdgeSet::validIndexes(const SoCoordinateElement* coords, const std::vector& pts) const { for (int32_t it : pts) { if (it >= coords->getNum()) { return false; } } return true; } void SoBrepEdgeSet::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; ctx->hl.clear(); touch(); } return; } const SoDetail* detail = hlaction->getElement(); if (!detail) { SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext); ctx->highlightColor = hlaction->getColor(); ctx->highlightIndex = INT_MAX; ctx->hl.clear(); ctx->hl.push_back(-1); touch(); return; } if (!detail->isOfType(SoLineDetail::getClassTypeId())) { SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext,false); if(ctx) { ctx->highlightIndex = -1; ctx->hl.clear(); touch(); } return; } SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext); ctx->highlightColor = hlaction->getColor(); int index = static_cast(detail)->getLineIndex(); const int32_t* cindices = this->coordIndex.getValues(0); int numcindices = this->coordIndex.getNum(); ctx->hl.clear(); for(int section=0,i=0;i index) break; }else if(section == index) ctx->hl.push_back(cindices[i]); } if(!ctx->hl.empty()) ctx->highlightIndex = index; else ctx->highlightIndex = -1; touch(); return; } else if (action->getTypeId() == Gui::SoSelectionElementAction::getClassTypeId()) { Gui::SoSelectionElementAction* selaction = static_cast(action); switch(selaction->getType()) { 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(); ctx->sl.clear(); touch(); } } return; } 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); // all ctx->sl.clear(); ctx->sl.push_back(-1); touch(); return; } case Gui::SoSelectionElementAction::Append: case Gui::SoSelectionElementAction::Remove: { const SoDetail* detail = selaction->getElement(); if (!detail || !detail->isOfType(SoLineDetail::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)->getLineIndex(); SelContextPtr ctx; if(selaction->getType() == Gui::SoSelectionElementAction::Append) { 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) return; }else{ ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext,false); if(!ctx || !ctx->removeIndex(index)) return; } ctx->sl.clear(); if(!ctx->selectionIndex.empty()) { const int32_t* cindices = this->coordIndex.getValues(0); int numcindices = this->coordIndex.getNum(); auto it = ctx->selectionIndex.begin(); for(int section=0,i=0;isl.push_back(cindices[i]); if(cindices[i] < 0) { if(++section > *it) { if(++it == ctx->selectionIndex.end()) break; } } } } touch(); break; } default : break; } return; } inherited::doAction(action); } SoDetail * SoBrepEdgeSet::createLineSegmentDetail(SoRayPickAction * action, const SoPrimitiveVertex * v1, const SoPrimitiveVertex * v2, SoPickedPoint * pp) { SoDetail* detail = inherited::createLineSegmentDetail(action, v1, v2, pp); SoLineDetail* line_detail = static_cast(detail); int index = line_detail->getLineIndex(); line_detail->setPartIndex(index); return detail; }