X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FModel%2FModel_AttributeSelection.cpp;h=9b0833a4a29605abce6b10cf259f6d95818ec7c4;hb=c3e944ee47c38c2ce7a18eb9fb0f643a4c2dcaf0;hp=d6f9f869eb5fdf184b293bcd64e754a05bf03006;hpb=5ad64d84635e116b2c012936e37cbf1cc3664c17;p=modules%2Fshaper.git diff --git a/src/Model/Model_AttributeSelection.cpp b/src/Model/Model_AttributeSelection.cpp index d6f9f869e..d2456c85e 100644 --- a/src/Model/Model_AttributeSelection.cpp +++ b/src/Model/Model_AttributeSelection.cpp @@ -1,67 +1,89 @@ -// Copyright (C) 2014-20xx CEA/DEN, EDF R&D - -// File: Model_AttributeSelection.h -// Created: 2 Oct 2014 -// Author: Mikhail PONIKAROV +// Copyright (C) 2014-2017 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or +// email : webmaster.salome@opencascade.com +// #include "Model_AttributeSelection.h" #include "Model_Application.h" #include "Model_Events.h" #include "Model_Data.h" #include "Model_Document.h" +#include "Model_SelectionNaming.h" +#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 +#include #include -#include #include -#include -#include #include #include -#include -#include -#include -#include -#include -#include #include #include -#include -#include #include -#include -using namespace std; +#include +#include +#include +#include + //#define DEB_NAMING 1 #ifdef DEB_NAMING #include #endif -/// adeed to the index in the packed map to signalize that the vertex of edge is seleted +/// added to the index in the packed map to signalize that the vertex of edge is selected /// (multiplied by the index of the edge) static const int kSTART_VERTEX_DELTA = 1000000; // identifier that there is simple reference: selection equals to context Standard_GUID kSIMPLE_REF_ID("635eacb2-a1d6-4dec-8348-471fae17cb29"); -Standard_GUID kCONSTUCTION_SIMPLE_REF_ID("635eacb2-a1d6-4dec-8348-471fae17cb28"); +// reference to Part sub-object +Standard_GUID kPART_REF_ID("635eacb2-a1d6-4dec-8348-471fae17cb27"); +// selection is invalid after recomputation +Standard_GUID kINVALID_SELECTION("bce47fd7-80fa-4462-9d63-2f58acddd49d"); + +// identifier of the selection of the center of circle on edge +Standard_GUID kCIRCLE_CENTER("d0d0e0f1-217a-4b95-8fbb-0c4132f23718"); +// identifier of the selection of the first focus point of ellipse on edge +Standard_GUID kELLIPSE_CENTER1("f70df04c-3168-4dc9-87a4-f1f840c1275d"); +// identifier of the selection of the second focus point of ellipse on edge +Standard_GUID kELLIPSE_CENTER2("1395ae73-8e02-4cf8-b204-06ff35873a32"); // on this label is stored: // TNaming_NamedShape - selected shape @@ -69,23 +91,40 @@ Standard_GUID kCONSTUCTION_SIMPLE_REF_ID("635eacb2-a1d6-4dec-8348-471fae17cb28") // TDataStd_IntPackedMap - indexes of edges in composite element (for construction) // TDataStd_Integer - type of the selected shape (for construction) // TDF_Reference - from ReferenceAttribute, the context -#define DDDD 1 -void Model_AttributeSelection::setValue(const ResultPtr& theContext, - const std::shared_ptr& theSubShape) +bool Model_AttributeSelection::setValue(const ResultPtr& theContext, + const std::shared_ptr& theSubShape, const bool theTemporarily) { - const std::shared_ptr& anOldShape = value(); + if (theTemporarily) { // just keep the stored without DF update + myTmpContext = theContext; + myTmpSubShape = theSubShape; + owner()->data()->sendAttributeUpdated(this); + return true; + } else { + myTmpContext.reset(); + myTmpSubShape.reset(); + myTmpCenterType = NOT_CENTER; + } + + CenterType aType; + const std::shared_ptr& anOldShape = internalValue(aType); bool isOldContext = theContext == myRef.value(); bool isOldShape = isOldContext && (theSubShape == anOldShape || (theSubShape && anOldShape && theSubShape->isEqual(anOldShape))); - if (isOldShape) return; // shape is the same, so context is also unchanged + if (isOldShape) return false; // shape is the same, so context is also unchanged + bool aToUnblock = false; // update the referenced object if needed - if (!isOldContext) + if (!isOldContext) { + aToUnblock = !owner()->data()->blockSendAttributeUpdated(true); myRef.setValue(theContext); + } // do noth use naming if selected shape is result shape itself, but not sub-shape TDF_Label aSelLab = selectionLabel(); aSelLab.ForgetAttribute(kSIMPLE_REF_ID); - aSelLab.ForgetAttribute(kCONSTUCTION_SIMPLE_REF_ID); + aSelLab.ForgetAttribute(kINVALID_SELECTION); + aSelLab.ForgetAttribute(kCIRCLE_CENTER); + aSelLab.ForgetAttribute(kELLIPSE_CENTER1); + aSelLab.ForgetAttribute(kELLIPSE_CENTER2); bool isDegeneratedEdge = false; // do not use the degenerated edge as a shape, a null context and shape is used in the case @@ -99,77 +138,268 @@ void Model_AttributeSelection::setValue(const ResultPtr& theContext, TDF_Label aRefLab = myRef.myRef->Label(); aSelLab.ForgetAllAttributes(true); myRef.myRef = TDF_Reference::Set(aSelLab.Father(), aSelLab.Father()); - return; + if (aToUnblock) + owner()->data()->blockSendAttributeUpdated(false); + return false; } if (theContext->groupName() == ModelAPI_ResultBody::group()) { // do not select the whole shape for body:it is already must be in the data framework - if (theContext->shape().get() && theContext->shape()->isEqual(theSubShape)) { + // equal and null selected objects mean the same: object is equal to context, + if (theContext->shape().get() && + (theContext->shape()->isEqual(theSubShape) || !theSubShape.get())) { aSelLab.ForgetAllAttributes(true); TDataStd_UAttribute::Set(aSelLab, kSIMPLE_REF_ID); } else { selectBody(theContext, theSubShape); } } else if (theContext->groupName() == ModelAPI_ResultConstruction::group()) { - if (!theSubShape.get()) { - // to sub, so the whole result is selected - aSelLab.ForgetAllAttributes(true); - TDataStd_UAttribute::Set(aSelLab, kCONSTUCTION_SIMPLE_REF_ID); - } else { - selectConstruction(theContext, theSubShape); + aSelLab.ForgetAllAttributes(true); // to remove old selection data + std::shared_ptr aConstruction = + std::dynamic_pointer_cast(theContext); + std::shared_ptr aSubShape; + if (theSubShape.get() && !theContext->shape()->isEqual(theSubShape)) + aSubShape = theSubShape; // the whole context + if (aConstruction->isInfinite()) { + // For correct naming selection, put the shape into the naming structure. + // It seems sub-shapes are not needed: only this shape is (and can be ) selected. + TNaming_Builder aBuilder(aSelLab); + aBuilder.Generated(theContext->shape()->impl()); } + int anIndex = aConstruction->select(theSubShape, owner()->document()); + TDataStd_Integer::Set(aSelLab, anIndex); + } else if (theContext->groupName() == ModelAPI_ResultPart::group()) { + aSelLab.ForgetAllAttributes(true); + TDataStd_UAttribute::Set(aSelLab, kPART_REF_ID); + selectPart(theContext, theSubShape); } - //the attribute initialized state should be changed by sendAttributeUpdated only - //myIsInitialized = true; owner()->data()->sendAttributeUpdated(this); - std::string aSelName = namingName(); - if(!aSelName.empty()) - TDataStd_Name::Set(selectionLabel(), aSelName.c_str()); //set name -#ifdef DDDD - //#### - //selectSubShape("FACE", "Extrusion_1/LateralFace_3"); - //selectSubShape("FACE", "Extrusion_1/TopFace"); - //selectSubShape("EDGE", "Extrusion_1/TopFace|Extrusion_1/LateralFace_1"); - //selectSubShape("EDGE", "Sketch_1/Edge_6"); -#endif + if (aToUnblock) + owner()->data()->blockSendAttributeUpdated(false); + + return true; +} + +void Model_AttributeSelection::setValueCenter( + const ResultPtr& theContext, const std::shared_ptr& theEdge, + const CenterType theCenterType, const bool theTemporarily) +{ + bool anUpdated = setValue(theContext, theEdge, theTemporarily); + if (theTemporarily) { + myTmpCenterType = theCenterType; + } else { // store in the data structure + TDF_Label aSelLab = selectionLabel(); + switch(theCenterType) { + case CIRCLE_CENTER: + if (!anUpdated) + anUpdated = !aSelLab.IsAttribute(kCIRCLE_CENTER); + TDataStd_UAttribute::Set(aSelLab, kCIRCLE_CENTER); + break; + case ELLIPSE_FIRST_FOCUS: + if (!anUpdated) + anUpdated = !aSelLab.IsAttribute(kELLIPSE_CENTER1); + TDataStd_UAttribute::Set(aSelLab, kELLIPSE_CENTER1); + break; + case ELLIPSE_SECOND_FOCUS: + if (!anUpdated) + anUpdated = !aSelLab.IsAttribute(kELLIPSE_CENTER2); + TDataStd_UAttribute::Set(aSelLab, kELLIPSE_CENTER2); + break; + } + if (anUpdated) + owner()->data()->sendAttributeUpdated(this); + } +} + +void Model_AttributeSelection::selectValue( + const std::shared_ptr& theSource) +{ + CenterType aType; + std::shared_ptr aValue = + std::dynamic_pointer_cast(theSource)->internalValue(aType); + if (!aValue.get() || aType == NOT_CENTER) { + setValue(theSource->context(), aValue); + } else { + std::shared_ptr anEdge(new GeomAPI_Edge); + anEdge->setImpl(new TopoDS_Shape(aValue->impl())); + setValueCenter(theSource->context(), anEdge, aType); + } +} + +void Model_AttributeSelection::removeTemporaryValues() +{ + if (myTmpContext.get() || myTmpSubShape.get()) { + myTmpContext.reset(); + myTmpSubShape.reset(); + } +} + +// returns the center of the edge: circular or elliptical +GeomShapePtr centerByEdge(GeomShapePtr theEdge, ModelAPI_AttributeSelection::CenterType theType) +{ + if (theType != ModelAPI_AttributeSelection::NOT_CENTER && theEdge.get() != NULL) { + TopoDS_Shape aShape = theEdge->impl(); + if (!aShape.IsNull() && aShape.ShapeType() == TopAbs_EDGE) { + TopoDS_Edge anEdge = TopoDS::Edge(aShape); + double aFirst, aLast; + Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast); + if (!aCurve.IsNull()) { + TopoDS_Vertex aVertex; + BRep_Builder aBuilder; + if (theType == ModelAPI_AttributeSelection::CIRCLE_CENTER) { + Handle(Geom_Circle) aCirc = Handle(Geom_Circle)::DownCast(aCurve); + if (!aCirc.IsNull()) { + aBuilder.MakeVertex(aVertex, aCirc->Location(), Precision::Confusion()); + } + } else { // ellipse + Handle(Geom_Ellipse) anEll = Handle(Geom_Ellipse)::DownCast(aCurve); + if (!anEll.IsNull()) { + aBuilder.MakeVertex(aVertex, + theType == ModelAPI_AttributeSelection::ELLIPSE_FIRST_FOCUS ? + anEll->Focus1() : anEll->Focus2(), Precision::Confusion()); + } + } + if (!aVertex.IsNull()) { + std::shared_ptr aResult(new GeomAPI_Vertex); + aResult->setImpl(new TopoDS_Vertex(aVertex)); + return aResult; + } + } + } + } + return theEdge; // no vertex, so, return the initial edge } std::shared_ptr Model_AttributeSelection::value() { - std::shared_ptr aResult; + if (!ModelAPI_AttributeSelection::isInitialized() && !myTmpContext.get() && !myTmpSubShape.get()) + return std::shared_ptr(); + CenterType aType = NOT_CENTER; + std::shared_ptr aResult = internalValue(aType); + return centerByEdge(aResult, aType); +} + +std::shared_ptr Model_AttributeSelection::internalValue(CenterType& theType) +{ + theType = NOT_CENTER; + GeomShapePtr aResult; + if (myTmpContext.get() || myTmpSubShape.get()) { + theType = myTmpCenterType; + ResultConstructionPtr aResulConstruction = + std::dynamic_pointer_cast(myTmpContext); + if(aResulConstruction.get()) { + // it is just reference to construction. + return myTmpSubShape; + } + return myTmpSubShape.get() ? myTmpSubShape : myTmpContext->shape(); + } + + TDF_Label aSelLab = selectionLabel(); + if (aSelLab.IsAttribute(kINVALID_SELECTION)) + return aResult; + + if (aSelLab.IsAttribute(kCIRCLE_CENTER)) + theType = CIRCLE_CENTER; + else if (aSelLab.IsAttribute(kELLIPSE_CENTER1)) + theType = ELLIPSE_FIRST_FOCUS; + else if (aSelLab.IsAttribute(kELLIPSE_CENTER2)) + theType = ELLIPSE_SECOND_FOCUS; + + if (myRef.isInitialized()) { - TDF_Label aSelLab = selectionLabel(); if (aSelLab.IsAttribute(kSIMPLE_REF_ID)) { // it is just reference to shape, not sub-shape ResultPtr aContext = context(); - if (!aContext.get()) + if (!aContext.get()) return aResult; // empty result return aContext->shape(); } - if (aSelLab.IsAttribute(kCONSTUCTION_SIMPLE_REF_ID)) { // it is just reference to construction, nothing is in value - return aResult; // empty result + if (aSelLab.IsAttribute(kPART_REF_ID)) { + ResultPartPtr aPart = std::dynamic_pointer_cast(context()); + if (!aPart.get() || !aPart->isActivated()) + return std::shared_ptr(); // postponed naming needed + Handle(TDataStd_Integer) anIndex; + if (aSelLab.FindAttribute(TDataStd_Integer::GetID(), anIndex)) { + if (anIndex->Get()) { // special selection attribute was created, use it + return aPart->selectionValue(anIndex->Get()); + } else { // face with name is already in the data model, so try to take it by name + Handle(TDataStd_Name) aName; + if (aSelLab.FindAttribute(TDataStd_Name::GetID(), aName)) { + std::string aSubShapeName(TCollection_AsciiString(aName->Get()).ToCString()); + std::size_t aPartEnd = aSubShapeName.find('/'); + if (aPartEnd != std::string::npos && aPartEnd != aSubShapeName.rfind('/')) { + std::string aNameInPart = aSubShapeName.substr(aPartEnd + 1); + int anIndex; + std::string aType; // to reuse already existing selection the type is not needed + return aPart->shapeInPart(aNameInPart, aType, anIndex); + } + } + } + } } + std::shared_ptr aConstr = + std::dynamic_pointer_cast(context()); + if (aConstr) { + if (aConstr->isInfinite()) + return aResult; // empty result + } Handle(TNaming_NamedShape) aSelection; - if (selectionLabel().FindAttribute(TNaming_NamedShape::GetID(), aSelection)) { + if (aSelLab.FindAttribute(TNaming_NamedShape::GetID(), aSelection)) { TopoDS_Shape aSelShape = aSelection->Get(); aResult = std::shared_ptr(new GeomAPI_Shape); aResult->setImpl(new TopoDS_Shape(aSelShape)); - } else { // for simple construction element: just shape of this construction element - ResultConstructionPtr aConstr = - std::dynamic_pointer_cast(context()); - if (aConstr) { - return aConstr->shape(); + } else if (aConstr) { // simple construction element: just shape of this construction element + Handle(TDataStd_Integer) anIndex; + if (aSelLab.FindAttribute(TDataStd_Integer::GetID(), anIndex)) { + if (anIndex->Get() == 0) // it is just reference to construction, nothing is in value + return aResult; + return aConstr->shape(anIndex->Get(), owner()->document()); } } } return aResult; } +bool Model_AttributeSelection::isInvalid() +{ + return selectionLabel().IsAttribute(kINVALID_SELECTION) == Standard_True; +} + +bool Model_AttributeSelection::isInitialized() +{ + if (ModelAPI_AttributeSelection::isInitialized()) { // additional checks if it is initialized + std::shared_ptr aResult; + if (myRef.isInitialized()) { + TDF_Label aSelLab = selectionLabel(); + if (aSelLab.IsAttribute(kSIMPLE_REF_ID)) { // it is just reference to shape, not sub-shape + ResultPtr aContext = context(); + return aContext.get() != NULL; + } + Handle(TNaming_NamedShape) aSelection; + if (selectionLabel().FindAttribute(TNaming_NamedShape::GetID(), aSelection)) { + return !aSelection->Get().IsNull(); + } else { // for simple construction element: just shape of this construction element + std::shared_ptr aConstr = + std::dynamic_pointer_cast(context()); + if (aConstr.get()) { + Handle(TDataStd_Integer) anIndex; + if (aSelLab.FindAttribute(TDataStd_Integer::GetID(), anIndex)) { + // for the whole shape it may return null, so, if index exists, returns true + return true; + } + } + } + } + } + return false; +} + Model_AttributeSelection::Model_AttributeSelection(TDF_Label& theLabel) : myRef(theLabel) { myIsInitialized = myRef.isInitialized(); + myParent = NULL; } void Model_AttributeSelection::setID(const std::string theID) @@ -179,7 +409,32 @@ void Model_AttributeSelection::setID(const std::string theID) } ResultPtr Model_AttributeSelection::context() { - return std::dynamic_pointer_cast(myRef.value()); + /* + if (!ModelAPI_AttributeSelection::isInitialized() && !myTmpContext.get() && !myTmpSubShape.get()) + return ResultPtr(); + */ + + if (myTmpContext.get() || myTmpSubShape.get()) { + return myTmpContext; + } + + ResultPtr aResult = std::dynamic_pointer_cast(myRef.value()); + // for parts there could be same-data result, so take the last enabled + if (aResult.get() && aResult->groupName() == ModelAPI_ResultPart::group()) { + int aSize = aResult->document()->size(ModelAPI_ResultPart::group()); + for(int a = aSize - 1; a >= 0; a--) { + ObjectPtr aPart = aResult->document()->object(ModelAPI_ResultPart::group(), a); + if (aPart.get() && aPart->data() == aResult->data()) { + ResultPtr aPartResult = std::dynamic_pointer_cast(aPart); + FeaturePtr anOwnerFeature = std::dynamic_pointer_cast(owner()); + // check that this result is not this-feature result (it is forbidden t oselect itself) + if (anOwnerFeature.get() && anOwnerFeature->firstResult() != aPartResult) { + return aPartResult; + } + } + } + } + return aResult; } @@ -192,211 +447,172 @@ void Model_AttributeSelection::setObject(const std::shared_ptr& TDF_LabelMap& Model_AttributeSelection::scope() { if (myScope.IsEmpty()) { // create a new scope if not yet done - // gets all labels with named shapes that are bofore this feature label (before in history) - TDF_Label aFeatureLab = std::dynamic_pointer_cast( - owner()->data())->label().Father(); - int aFeatureID = aFeatureLab.Tag(); - TDF_ChildIterator aFeaturesIter(aFeatureLab.Father()); - for(; aFeaturesIter.More(); aFeaturesIter.Next()) { - if (aFeaturesIter.Value().Tag() >= aFeatureID) // the left labels are created later - break; - TDF_ChildIDIterator aNSIter(aFeaturesIter.Value(), TNaming_NamedShape::GetID(), 1); - for(; aNSIter.More(); aNSIter.Next()) { - Handle(TNaming_NamedShape) aNS = Handle(TNaming_NamedShape)::DownCast(aNSIter.Value()); - if (!aNS.IsNull() && aNS->Evolution() != TNaming_SELECTED) { - myScope.Add(aNS->Label()); + // gets all features with named shapes that are before this feature label (before in history) + DocumentPtr aMyDoc = owner()->document(); + std::list > allFeatures = aMyDoc->allFeatures(); + std::list >::iterator aFIter = allFeatures.begin(); + bool aMePassed = false; + CompositeFeaturePtr aComposite = + std::dynamic_pointer_cast(owner()); + FeaturePtr aFeature = std::dynamic_pointer_cast(owner()); + CompositeFeaturePtr aCompositeOwner, aCompositeOwnerOwner; + if (aFeature.get()) { + aCompositeOwner = ModelAPI_Tools::compositeOwner(aFeature); + if (aCompositeOwner.get()) { + aCompositeOwnerOwner = ModelAPI_Tools::compositeOwner(aCompositeOwner); + } + } + // for group Scope is not limitet: this is always up to date objects + // this causes problem in galeries.py + //bool isGroup = aFeature.get() && aFeature->getKind() == "Group"; + for(; aFIter != allFeatures.end(); aFIter++) { + if (*aFIter == owner()) { // the left features are created later (except subs of composite) + aMePassed = true; + continue; + } + //if (isGroup) aMePassed = false; + bool isInScope = !aMePassed; + if (!isInScope && aComposite.get()) { + // try to add sub-elements of composite if this is composite + if (aComposite->isSub(*aFIter)) + isInScope = true; + } + // remove the composite-owner of this feature (sketch in extrusion-cut) + if (isInScope && (aCompositeOwner == *aFIter || aCompositeOwnerOwner == *aFIter)) + isInScope = false; + + if (isInScope && aFIter->get() && (*aFIter)->data()->isValid()) { + TDF_Label aFeatureLab = std::dynamic_pointer_cast( + (*aFIter)->data())->label().Father(); + TDF_ChildIDIterator aNSIter(aFeatureLab, TNaming_NamedShape::GetID(), true); + for(; aNSIter.More(); aNSIter.Next()) { + Handle(TNaming_NamedShape) aNS = Handle(TNaming_NamedShape)::DownCast(aNSIter.Value()); + if (!aNS.IsNull() && aNS->Evolution() != TNaming_SELECTED) { + myScope.Add(aNS->Label()); + } } } } + // also add all naming labels created for external constructions + std::shared_ptr aDoc = std::dynamic_pointer_cast(aMyDoc); + TDF_Label anExtDocLab = aDoc->extConstructionsLabel(); + TDF_ChildIDIterator aNSIter(anExtDocLab, TNaming_NamedShape::GetID(), true); + for(; aNSIter.More(); aNSIter.Next()) { + Handle(TNaming_NamedShape) aNS = Handle(TNaming_NamedShape)::DownCast(aNSIter.Value()); + if (!aNS.IsNull() && aNS->Evolution() != TNaming_SELECTED) { + myScope.Add(aNS->Label()); + } + } } return myScope; } +/// Sets the invalid flag if flag is false, or removes it if "true" +/// Returns theFlag +static bool setInvalidIfFalse(TDF_Label& theLab, const bool theFlag) { + if (theFlag) { + theLab.ForgetAttribute(kINVALID_SELECTION); + } else { + TDataStd_UAttribute::Set(theLab, kINVALID_SELECTION); + } + return theFlag; +} + +void Model_AttributeSelection::split( + ResultPtr theContext, TopoDS_Shape theNewShape, TopAbs_ShapeEnum theType) +{ + TopTools_ListOfShape aSubs; + for(TopoDS_Iterator anExplorer(theNewShape); anExplorer.More(); anExplorer.Next()) { + if (!anExplorer.Value().IsNull() && + anExplorer.Value().ShapeType() == theType) { + aSubs.Append(anExplorer.Value()); + } else { // invalid case; bad result shape, so, impossible to split easily + aSubs.Clear(); + break; + } + } + if (aSubs.Extent() > 1) { // ok to split + TopTools_ListIteratorOfListOfShape aSub(aSubs); + GeomShapePtr aSubSh(new GeomAPI_Shape); + aSubSh->setImpl(new TopoDS_Shape(aSub.Value())); + setValue(theContext, aSubSh); + for(aSub.Next(); aSub.More(); aSub.Next()) { + GeomShapePtr aSubSh(new GeomAPI_Shape); + aSubSh->setImpl(new TopoDS_Shape(aSub.Value())); + myParent->append(theContext, aSubSh); + } + } +} + bool Model_AttributeSelection::update() { - ResultPtr aContext = context(); - if (!aContext.get()) return false; TDF_Label aSelLab = selectionLabel(); + ResultPtr aContext = context(); + if (!aContext.get()) + return setInvalidIfFalse(aSelLab, false); if (aSelLab.IsAttribute(kSIMPLE_REF_ID)) { // it is just reference to shape, not sub-shape - return aContext->shape() && !aContext->shape()->isNull(); + return setInvalidIfFalse(aSelLab, aContext->shape() && !aContext->shape()->isNull()); } - if (aSelLab.IsAttribute(kCONSTUCTION_SIMPLE_REF_ID)) { // it is just reference to construction, not sub-shape - return aContext->shape() && !aContext->shape()->isNull(); + + if (aSelLab.IsAttribute(kPART_REF_ID)) { // it is reference to the part object + std::shared_ptr aNoSelection; + bool aResult = selectPart(aContext, aNoSelection, true); + aResult = setInvalidIfFalse(aSelLab, aResult); + if (aResult) { + owner()->data()->sendAttributeUpdated(this); + } + return aResult; } if (aContext->groupName() == ModelAPI_ResultBody::group()) { // body: just a named shape, use selection mechanism from OCCT TNaming_Selector aSelector(aSelLab); + TopoDS_Shape anOldShape; + if (!aSelector.NamedShape().IsNull()) { + anOldShape = aSelector.NamedShape()->Get(); + } bool aResult = aSelector.Solve(scope()) == Standard_True; - owner()->data()->sendAttributeUpdated(this); - return aResult; - } else if (aContext->groupName() == ModelAPI_ResultConstruction::group()) { - // construction: identification by the results indexes, recompute faces and - // take the face that more close by the indexes - ResultConstructionPtr aConstructionContext = - std::dynamic_pointer_cast(aContext); - FeaturePtr aContextFeature = aContext->document()->feature(aContext); - // sketch sub-element - if (aConstructionContext && - std::dynamic_pointer_cast(aContextFeature).get()) - { - TDF_Label aLab = myRef.myRef->Label(); - // getting a type of selected shape - Handle(TDataStd_Integer) aTypeAttr; - if (!aLab.FindAttribute(TDataStd_Integer::GetID(), aTypeAttr)) { - return false; - } - TopAbs_ShapeEnum aShapeType = (TopAbs_ShapeEnum)(aTypeAttr->Get()); - // selected indexes will be needed in each "if" - Handle(TDataStd_IntPackedMap) aSubIds; - std::shared_ptr aNewSelected; - bool aNoIndexes = - !aLab.FindAttribute(TDataStd_IntPackedMap::GetID(), aSubIds) || aSubIds->Extent() == 0; - // for now working only with composite features - CompositeFeaturePtr aComposite = - std::dynamic_pointer_cast(aContextFeature); - if (!aComposite.get() || aComposite->numberOfSubs() == 0) { - return false; + // must be before sending of updated attribute (1556) + aResult = setInvalidIfFalse(aSelLab, aResult); + TopoDS_Shape aNewShape; + if (!aSelector.NamedShape().IsNull()) { + aNewShape = aSelector.NamedShape()->Get(); + } + if (anOldShape.IsNull() || aNewShape.IsNull() || + !anOldShape.IsEqual(aSelector.NamedShape()->Get())) { + // shape type shoud not not changed: if shape becomes compound of such shapes, then split + if (myParent && !anOldShape.IsNull() && !aNewShape.IsNull() && + anOldShape.ShapeType() != aNewShape.ShapeType() && + (aNewShape.ShapeType() == TopAbs_COMPOUND || aNewShape.ShapeType() == TopAbs_COMPSOLID)) + { + split(aContext, aNewShape, anOldShape.ShapeType()); } + owner()->data()->sendAttributeUpdated(this); // send updated if shape is changed + } + return aResult; + } - if (aShapeType == TopAbs_FACE) { // compound is for the whole sketch selection - // If this is a wire with plane defined thin it is a sketch-like object - if (!aConstructionContext->facesNum()) // no faces, update can not work correctly - return false; - // if there is no edges indexes, any face can be used: take the first - std::shared_ptr aNewSelected; - if (aNoIndexes) { - aNewSelected = aConstructionContext->face(0); - } else { // searching for most looks-like initial face by the indexes - // prepare edges of the current resut for the fast searching - TColStd_MapOfTransient allCurves; - const int aSubNum = aComposite->numberOfSubs(); - for(int a = 0; a < aSubNum; a++) { - if (aSubIds->Contains(aComposite->subFeatureId(a))) { - FeaturePtr aSub = aComposite->subFeature(a); - const std::list >& aResults = aSub->results(); - std::list >::const_iterator aRes; - for(aRes = aResults.cbegin(); aRes != aResults.cend(); aRes++) { - ResultConstructionPtr aConstr = - std::dynamic_pointer_cast(*aRes); - if (aConstr->shape() && aConstr->shape()->isEdge()) { - const TopoDS_Shape& aResShape = aConstr->shape()->impl(); - TopoDS_Edge anEdge = TopoDS::Edge(aResShape); - if (!anEdge.IsNull()) { - Standard_Real aFirst, aLast; - Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast); - allCurves.Add(aCurve); - } - } - } - } - } - double aBestFound = 0; // best percentage of found edges - for(int aFaceIndex = 0; aFaceIndex < aConstructionContext->facesNum(); aFaceIndex++) { - int aFound = 0, aNotFound = 0; - TopExp_Explorer anEdgesExp( - aConstructionContext->face(aFaceIndex)->impl(), TopAbs_EDGE); - for(; anEdgesExp.More(); anEdgesExp.Next()) { - TopoDS_Edge anEdge = TopoDS::Edge(anEdgesExp.Current()); - if (!anEdge.IsNull()) { - Standard_Real aFirst, aLast; - Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast); - if (allCurves.Contains(aCurve)) { - aFound++; - } else { - aNotFound++; - } - } - } - if (aFound + aNotFound != 0) { - double aPercentage = double(aFound) / double(aFound + aNotFound); - if (aPercentage > aBestFound) { - aBestFound = aPercentage; - aNewSelected = aConstructionContext->face(aFaceIndex); - } - } - } - } - if (aNewSelected) { // store this new selection - selectConstruction(aContext, aNewSelected); - owner()->data()->sendAttributeUpdated(this); - return true; - } - } else if (aShapeType == TopAbs_EDGE) { - // just reselect the edge by the id - const int aSubNum = aComposite->numberOfSubs(); - for(int a = 0; a < aSubNum; a++) { - // if aSubIds take any, the first appropriate - if (aSubIds->IsEmpty() || aSubIds->Contains(aComposite->subFeatureId(a))) { - // found the appropriate feature - FeaturePtr aFeature = aComposite->subFeature(a); - std::list >::const_iterator aResIter = - aFeature->results().cbegin(); - for(;aResIter != aFeature->results().cend(); aResIter++) { - ResultConstructionPtr aRes = - std::dynamic_pointer_cast(*aResIter); - if (aRes && aRes->shape() && aRes->shape()->isEdge()) { // found! - selectConstruction(aContext, aRes->shape()); - owner()->data()->sendAttributeUpdated(this); - return true; - } - } - } - } - } else if (aShapeType == TopAbs_VERTEX) { - // just reselect the vertex by the id of edge - const int aSubNum = aComposite->numberOfSubs(); - for(int a = 0; a < aSubNum; a++) { - // if aSubIds take any, the first appropriate - int aFeatureID = aComposite->subFeatureId(a); - if (aSubIds->IsEmpty() || aSubIds->Contains(aFeatureID) || - aSubIds->Contains(aFeatureID + kSTART_VERTEX_DELTA) || - aSubIds->Contains(aFeatureID + kSTART_VERTEX_DELTA * 2)) { - // searching for deltas - int aVertexNum = 0; - if (aSubIds->Contains(aFeatureID + kSTART_VERTEX_DELTA)) aVertexNum = 1; - else if (aSubIds->Contains(aFeatureID + kSTART_VERTEX_DELTA * 2)) aVertexNum = 2; - // found the feature with appropriate edge - FeaturePtr aFeature = aComposite->subFeature(a); - std::list >::const_iterator aResIter = - aFeature->results().cbegin(); - for(;aResIter != aFeature->results().cend(); aResIter++) { - ResultConstructionPtr aRes = - std::dynamic_pointer_cast(*aResIter); - if (aRes && aRes->shape()) { - if (aRes->shape()->isVertex() && aVertexNum == 0) { // found! - selectConstruction(aContext, aRes->shape()); - owner()->data()->sendAttributeUpdated(this); - return true; - } else if (aRes->shape()->isEdge() && aVertexNum > 0) { - const TopoDS_Shape& anEdge = aRes->shape()->impl(); - int aVIndex = 1; - for(TopExp_Explorer aVExp(anEdge, TopAbs_VERTEX); aVExp.More(); aVExp.Next()) { - if (aVIndex == aVertexNum) { // found! - std::shared_ptr aVertex(new GeomAPI_Shape); - aVertex->setImpl(new TopoDS_Shape(aVExp.Current())); - selectConstruction(aContext, aVertex); - owner()->data()->sendAttributeUpdated(this); - return true; - } - aVIndex++; - } - } - } - } - } - } + if (aContext->groupName() == ModelAPI_ResultConstruction::group()) { + Handle(TDataStd_Integer) anIndex; + if (aSelLab.FindAttribute(TDataStd_Integer::GetID(), anIndex)) { + std::shared_ptr aConstructionContext = + std::dynamic_pointer_cast(aContext); + bool aModified = true; + bool aValid = aConstructionContext->update(anIndex->Get(), owner()->document(), aModified); + setInvalidIfFalse(aSelLab, aValid); + if (aConstructionContext->isInfinite()) { + // Update the selected shape. + TNaming_Builder aBuilder(aSelLab); + aBuilder.Generated(aConstructionContext->shape()->impl()); } - } else { // simple construction element: the selected is that needed - selectConstruction(aContext, aContext->shape()); - owner()->data()->sendAttributeUpdated(this); - return true; + if (aModified) + owner()->data()->sendAttributeUpdated(this); + return aValid; } } - return false; // unknown case + return setInvalidIfFalse(aSelLab, false); // unknown case } - void Model_AttributeSelection::selectBody( const ResultPtr& theContext, const std::shared_ptr& theSubShape) { @@ -404,172 +620,136 @@ void Model_AttributeSelection::selectBody( TNaming_Selector aSel(selectionLabel()); TopoDS_Shape aContext; - ResultBodyPtr aBody = std::dynamic_pointer_cast(myRef.value()); + ResultBodyPtr aBody = std::dynamic_pointer_cast(theContext);//myRef.value() if (aBody) { aContext = aBody->shape()->impl(); } else { - ResultPtr aResult = + ResultPtr aResult = std::dynamic_pointer_cast(myRef.value()); if (aResult) { aContext = aResult->shape()->impl(); } else { - Events_Error::send("A result with shape is expected"); + Events_InfoMessage("Model_AttributeSelection", "A result with shape is expected").send(); return; } } - TopoDS_Shape aNewShape = theSubShape ? theSubShape->impl() : aContext; - /// fix for issue 411: result modified shapes must not participate in this selection mechanism - FeaturePtr aFeatureOwner = std::dynamic_pointer_cast(owner()); - if (aFeatureOwner.get()) - aFeatureOwner->eraseResults(); - if (!aContext.IsNull()) { - aSel.Select(aNewShape, aContext); + + // with "recover" feature the selected context may be not up to date (issue 1710) + Handle(TNaming_NamedShape) aResult; + TDF_Label aSelLab = selectionLabel(); + TopoDS_Shape aNewContext = aContext; + bool isUpdated = true; + while(!aNewContext.IsNull() && isUpdated) { + // searching for the very last shape that was produced from this one + isUpdated = false; + if (!TNaming_Tool::HasLabel(aSelLab, aNewContext)) + // to avoid crash of TNaming_SameShapeIterator if pure shape does not exists + break; + for(TNaming_SameShapeIterator anIter(aNewContext, aSelLab); anIter.More(); anIter.Next()) { + TDF_Label aNSLab = anIter.Label(); + if (!scope().Contains(aNSLab)) + continue; + Handle(TNaming_NamedShape) aNS; + if (aNSLab.FindAttribute(TNaming_NamedShape::GetID(), aNS)) { + for(TNaming_Iterator aShapesIter(aNS); aShapesIter.More(); aShapesIter.Next()) { + if (aShapesIter.Evolution() == TNaming_SELECTED) + continue; // don't use the selection evolution + if (!aShapesIter.OldShape().IsNull() && aShapesIter.OldShape().IsSame(aNewContext)) { + // found the original shape + aNewContext = aShapesIter.NewShape(); // go to the newer shape + isUpdated = true; + break; + } + } + } + } + } + if (aNewContext.IsNull()) { // a context is already deleted + setInvalidIfFalse(aSelLab, false); + Events_InfoMessage("Model_AttributeSelection", "Failed to select shape already deleted").send(); + return; } -} -/// registers the name of the shape in the label (theID == 0) of sub label (theID is a tag) -/// if theID is zero, -static void registerSubShape(TDF_Label theMainLabel, TopoDS_Shape theShape, - const int theID, const FeaturePtr& theContextFeature, std::shared_ptr theDoc, - std::string theAdditionalName, - Handle(TDataStd_IntPackedMap) theRefs = Handle(TDataStd_IntPackedMap)()) -{ - TDF_Label aLab = theID == 0 ? theMainLabel : theMainLabel.FindChild(theID); - TNaming_Builder aBuilder(aLab); - aBuilder.Generated(theShape); - std::stringstream aName; - aName<name()<<"/"; - if (!theAdditionalName.empty()) - aName<GetMap()); - for(; aRef.More(); aRef.Next()) { - aName<<"-"<impl() : aContext; + if (!aNewSub.IsEqual(aContext)) { // searching for subshape in the new context + bool isFound = false; + TopExp_Explorer anExp(aNewContext, aNewSub.ShapeType()); + for(; anExp.More(); anExp.Next()) { + if (anExp.Current().IsSame(aNewSub)) { + isFound = true; + break; + } + } + if (!isFound) { // sub-shape is not found in the up-to-date instance of the context shape + // if context is sub-result of compound/compsolid, selection of sub-shape better propagate to + // the main result (which is may be modified), case is in 1799 + ResultCompSolidPtr aMain = ModelAPI_Tools::compSolidOwner(theContext); + if (aMain.get()) { + selectBody(aMain, theSubShape); + return; + } + setInvalidIfFalse(aSelLab, false); + Events_InfoMessage("Model_AttributeSelection", + "Failed to select sub-shape already modified").send(); + return; } } - theDoc->addNamingName(aLab, aName.str()); - TDataStd_Name::Set(aLab, aName.str().c_str()); -} + /// fix for issue 411: result modified shapes must not participate in this selection mechanism + if (!aContext.IsNull()) { + FeaturePtr aFeatureOwner = std::dynamic_pointer_cast(owner()); + bool aEraseResults = false; + if (aFeatureOwner.get()) { + aEraseResults = !aFeatureOwner->results().empty(); + if (aEraseResults) // erase results without flash deleted and redisplay: do it after Select + aFeatureOwner->removeResults(0, false, false); + } + aSel.Select(aNewSub, aNewContext); -void Model_AttributeSelection::selectConstruction( - const ResultPtr& theContext, const std::shared_ptr& theSubShape) -{ - std::shared_ptr aMyDoc = - std::dynamic_pointer_cast(owner()->document()); - FeaturePtr aContextFeature = theContext->document()->feature(theContext); - CompositeFeaturePtr aComposite = - std::dynamic_pointer_cast(aContextFeature); - const TopoDS_Shape& aSubShape = theSubShape->impl(); - if (!aComposite || aComposite->numberOfSubs() == 0) { - // saving of context is enough: result construction contains exactly the needed shape - TNaming_Builder aBuilder(selectionLabel()); - aBuilder.Generated(aSubShape); - aMyDoc->addNamingName(selectionLabel(), theContext->data()->name()); - TDataStd_Name::Set(selectionLabel(), theContext->data()->name().c_str()); - return; - } - std::shared_ptr aData = std::dynamic_pointer_cast(owner()->data()); - TDF_Label aLab = myRef.myRef->Label(); - // identify the reuslts of sub-object of the composite by edges - // save type of the selected shape in integer attribute - TopAbs_ShapeEnum aShapeType = aSubShape.ShapeType(); - TDataStd_Integer::Set(aLab, (int)aShapeType); - gp_Pnt aVertexPos; - TColStd_MapOfTransient allCurves; - if (aShapeType == TopAbs_VERTEX) { // compare positions - aVertexPos = BRep_Tool::Pnt(TopoDS::Vertex(aSubShape)); - } else { - for(TopExp_Explorer anEdgeExp(aSubShape, TopAbs_EDGE); anEdgeExp.More(); anEdgeExp.Next()) { - TopoDS_Edge anEdge = TopoDS::Edge(anEdgeExp.Current()); - Standard_Real aFirst, aLast; - Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast); - allCurves.Add(aCurve); + if (aEraseResults) { // flash after Select : in Groups it makes selection with shift working + static Events_Loop* aLoop = Events_Loop::loop(); + static const Events_ID kDeletedEvent = aLoop->eventByName(EVENT_OBJECT_DELETED); + aLoop->flush(kDeletedEvent); } } - // iterate and store the result ids of sub-elements and sub-elements to sub-labels - Handle(TDataStd_IntPackedMap) aRefs = TDataStd_IntPackedMap::Set(aLab); - aRefs->Clear(); - const int aSubNum = aComposite->numberOfSubs(); - for(int a = 0; a < aSubNum; a++) { - FeaturePtr aSub = aComposite->subFeature(a); - const std::list >& aResults = aSub->results(); - std::list >::const_iterator aRes = aResults.cbegin(); - // there may be many shapes (circle and center): register if at least one is in selection - for(; aRes != aResults.cend(); aRes++) { - ResultConstructionPtr aConstr = - std::dynamic_pointer_cast(*aRes); - if (!aConstr->shape()) { - continue; - } - if (aShapeType == TopAbs_VERTEX) { - if (aConstr->shape()->isVertex()) { // compare vertices positions - const TopoDS_Shape& aVertex = aConstr->shape()->impl(); - gp_Pnt aPnt = BRep_Tool::Pnt(TopoDS::Vertex(aVertex)); - if (aPnt.IsEqual(aVertexPos, Precision::Confusion())) { - aRefs->Add(aComposite->subFeatureId(a)); - } - } else { // get first or last vertex of the edge: last is stored with negative sign - const TopoDS_Shape& anEdge = aConstr->shape()->impl(); - int aDelta = kSTART_VERTEX_DELTA; - for(TopExp_Explorer aVExp(anEdge, TopAbs_VERTEX); aVExp.More(); aVExp.Next()) { - gp_Pnt aPnt = BRep_Tool::Pnt(TopoDS::Vertex(aVExp.Current())); - if (aPnt.IsEqual(aVertexPos, Precision::Confusion())) { - aRefs->Add(aDelta + aComposite->subFeatureId(a)); - break; - } - aDelta += kSTART_VERTEX_DELTA; - } - } +} + +bool Model_AttributeSelection::selectPart( + const ResultPtr& theContext, const std::shared_ptr& theSubShape, + const bool theUpdate) +{ + ResultPartPtr aPart = std::dynamic_pointer_cast(theContext); + if (!aPart.get() || !aPart->isActivated()) + return true; // postponed naming + if (theUpdate) { + Handle(TDataStd_Integer) anIndex; + if (selectionLabel().FindAttribute(TDataStd_Integer::GetID(), anIndex)) { + // by internal selection + if (anIndex->Get() > 0) { + // update the selection by index + return aPart->updateInPart(anIndex->Get()); } else { - if (aConstr->shape()->isEdge()) { - const TopoDS_Shape& aResShape = aConstr->shape()->impl(); - TopoDS_Edge anEdge = TopoDS::Edge(aResShape); - if (!anEdge.IsNull()) { - Standard_Real aFirst, aLast; - Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast); - if (allCurves.Contains(aCurve)) { - int anID = aComposite->subFeatureId(a); - aRefs->Add(anID); - if (aShapeType != TopAbs_EDGE) { // edge do not need the sub-edges on sub-labels - // add edges to sub-label to support naming for edges selection - TopExp_Explorer anEdgeExp(aSubShape, TopAbs_EDGE); - for(; anEdgeExp.More(); anEdgeExp.Next()) { - TopoDS_Edge anEdge = TopoDS::Edge(anEdgeExp.Current()); - Standard_Real aFirst, aLast; - Handle(Geom_Curve) aFaceCurve = BRep_Tool::Curve(anEdge, aFirst, aLast); - if (aFaceCurve == aCurve) { - registerSubShape(selectionLabel(), anEdge, anID, aContextFeature, aMyDoc, ""); - } - } - } else { // put vertices of the selected edge to sub-labels - // add edges to sub-label to support naming for edges selection - TopExp_Explorer anEdgeExp(aSubShape, TopAbs_VERTEX); - int aTagIndex = anID + kSTART_VERTEX_DELTA; - for(; anEdgeExp.More(); anEdgeExp.Next(), aTagIndex += kSTART_VERTEX_DELTA) { - TopoDS_Vertex aV = TopoDS::Vertex(anEdgeExp.Current()); - - std::stringstream anAdditionalName; - registerSubShape(selectionLabel(), aV, aTagIndex, aContextFeature, aMyDoc, - ""); - } - } - } - } - } + return true; // nothing to do, referencing just by name } } + return true; // nothing to do, referencing just by name + } + // store the shape (in case part is not loaded it should be useful + TopoDS_Shape aShape; + std::string aName = theContext->data()->name(); + if (!theSubShape.get() || theSubShape->isNull()) {// the whole part shape is selected + aShape = theContext->shape()->impl(); + } else { + aShape = theSubShape->impl(); + int anIndex; + aName += "/" + aPart->nameInPart(theSubShape, anIndex); + TDataStd_Integer::Set(selectionLabel(), anIndex); } - // store the selected as primitive TNaming_Builder aBuilder(selectionLabel()); - aBuilder.Generated(aSubShape); - registerSubShape(selectionLabel(), aSubShape, 0, aContextFeature, aMyDoc, "", aRefs); + aBuilder.Select(aShape, aShape); + // identify by name in the part + TDataStd_Name::Set(selectionLabel(), aName.c_str()); + return !aName.empty(); } TDF_Label Model_AttributeSelection::selectionLabel() @@ -577,562 +757,609 @@ TDF_Label Model_AttributeSelection::selectionLabel() return myRef.myRef->Label().FindChild(1); } -#define FIX_BUG1 1 -std::string GetShapeName(std::shared_ptr theDoc, const TopoDS_Shape& theShape, - const TDF_Label& theLabel) -{ - std::string aName; - // check if the subShape is already in DF - Handle(TNaming_NamedShape) aNS = TNaming_Tool::NamedShape(theShape, theLabel); - Handle(TDataStd_Name) anAttr; - if(!aNS.IsNull() && !aNS->IsEmpty()) { // in the document - if(aNS->Label().FindAttribute(TDataStd_Name::GetID(), anAttr)) { - aName = TCollection_AsciiString(anAttr->Get()).ToCString(); - if(!aName.empty()) { - const TDF_Label& aLabel = theDoc->findNamingName(aName); - /* MPV: the same shape with the same name may be duplicated on different labels (selection of the same construction object) - if(!aLabel.IsEqual(aNS->Label())) { - //aName.erase(); //something is wrong, to be checked!!! - aName += "_SomethingWrong"; - return aName; - }*/ - - static const std::string aPostFix("_"); - TNaming_Iterator anItL(aNS); - for(int i = 1; anItL.More(); anItL.Next(), i++) { - if(anItL.NewShape() == theShape) { - aName += aPostFix; - aName += TCollection_AsciiString (i).ToCString(); - break; - } - } - /* MPV: the code below does not work because of TNaming_Tool.cxx line 145: aNS->Get() uses maps - const TopoDS_Shape& aShape = aNS->Get(); - if(aShape.ShapeType() == TopAbs_COMPOUND) { - std::string aPostFix("_"); - TopoDS_Iterator it(aShape); - for (int i = 1;it.More();it.Next(), i++) { - if(it.Value() == theShape) { - aPostFix += TCollection_AsciiString (i).ToCString(); - aName += aPostFix; - break; - } - else continue; - } - } - */ - } - } - } - return aName; -} +/// prefixes of the shape names with centers defined +static std::map kCENTERS_PREFIX; -bool isTrivial (const TopTools_ListOfShape& theAncestors, TopTools_IndexedMapOfShape& theSMap) +/// returns the map that contains all possible prefixes of the center-names +static std::map& centersMap() { - // a trivial case: F1 & F2, aNumber = 1, i.e. intersection gives 1 edge. - TopoDS_Compound aCmp; - BRep_Builder BB; - BB.MakeCompound(aCmp); - TopTools_ListIteratorOfListOfShape it(theAncestors); - for(;it.More();it.Next()) { - BB.Add(aCmp, it.Value()); - theSMap.Add(it.Value()); - } - int aNumber(0); - TopTools_IndexedDataMapOfShapeListOfShape aMap2; - TopExp::MapShapesAndAncestors(aCmp, TopAbs_EDGE, TopAbs_FACE, aMap2); - for (int i = 1; i <= aMap2.Extent(); i++) { - const TopoDS_Shape& aKey = aMap2.FindKey(i); - const TopTools_ListOfShape& anAncestors = aMap2.FindFromIndex(i); - if(anAncestors.Extent() > 1) aNumber++; + if (kCENTERS_PREFIX.empty()) { // fill map by initial values + kCENTERS_PREFIX[ModelAPI_AttributeSelection::CIRCLE_CENTER] = "__cc"; + kCENTERS_PREFIX[ModelAPI_AttributeSelection::ELLIPSE_FIRST_FOCUS] = "__eff"; + kCENTERS_PREFIX[ModelAPI_AttributeSelection::ELLIPSE_SECOND_FOCUS] = "__esf"; } - if(aNumber > 1) return false; - return true; + return kCENTERS_PREFIX; } -std::string Model_AttributeSelection::namingName() + +std::string Model_AttributeSelection::namingName(const std::string& theDefaultName) { std::string aName(""); - if(!this->isInitialized()) return aName; - Handle(TDataStd_Name) anAtt; - if(selectionLabel().FindAttribute(TDataStd_Name::GetID(), anAtt)) { - aName = TCollection_AsciiString(anAtt->Get()).ToCString(); - return aName; - } + if(!this->isInitialized()) + return !theDefaultName.empty() ? theDefaultName : aName; - std::shared_ptr aSubSh = value(); + CenterType aCenterType = NOT_CENTER; + std::shared_ptr aSubSh = internalValue(aCenterType); ResultPtr aCont = context(); - aName = "Undefined name"; - if(!aCont.get() || aCont->shape()->isNull()) - return aName; - if (!aSubSh.get() || aSubSh->isNull()) { // no subshape, so just the whole feature name - return aCont->data()->name(); + + if (!aCont.get()) // in case of selection of removed result + return ""; + + Model_SelectionNaming aSelNaming(selectionLabel()); + std::string aResult = aSelNaming.namingName( + aCont, aSubSh, theDefaultName, owner()->document() != aCont->document()); + if (aCenterType != NOT_CENTER) { + aResult += centersMap()[aCenterType]; } - TopoDS_Shape aSubShape = aSubSh->impl(); - TopoDS_Shape aContext = aCont->shape()->impl(); -#ifdef DEB_NAMING - if(aSubShape.ShapeType() == TopAbs_COMPOUND) { - BRepTools::Write(aSubShape, "Selection.brep"); - BRepTools::Write(aContext, "Context.brep"); + return aResult; +} + +// returns the center type and modifies the shape name if this name is center-name +static ModelAPI_AttributeSelection::CenterType centerTypeByName(std::string& theShapeName) +{ + std::map::iterator aPrefixIter = + centersMap().begin(); + for(; aPrefixIter != centersMap().end(); aPrefixIter++) { + std::size_t aFound = theShapeName.find(aPrefixIter->second); + if (aFound != std::string::npos && + aFound == theShapeName.size() - aPrefixIter->second.size()) { + theShapeName = theShapeName.substr(0, aFound); + return aPrefixIter->first; + } } -#endif - std::shared_ptr aDoc = - std::dynamic_pointer_cast(aCont->document()); - - // check if the subShape is already in DF - aName = GetShapeName(aDoc, aSubShape, selectionLabel()); - if(aName.empty() ) { // not in the document! - TopAbs_ShapeEnum aType = aSubShape.ShapeType(); - switch (aType) { - case TopAbs_FACE: - // the Face should be in DF. If it is not the case, it is an error ==> to be debugged - break; - case TopAbs_EDGE: - { - // name structure: F1 | F2 [| F3 | F4], where F1 & F2 the faces which gives the Edge in trivial case - // if it is not atrivial case we use localization by neighbours. F3 & F4 - neighbour faces - if (BRep_Tool::Degenerated(TopoDS::Edge(aSubShape))) { - aName = "Degenerated_Edge"; - break; - } - TopTools_IndexedDataMapOfShapeListOfShape aMap; - TopExp::MapShapesAndAncestors(aContext, TopAbs_EDGE, TopAbs_FACE, aMap); - TopTools_IndexedMapOfShape aSMap; // map for ancestors of the sub-shape - bool isTrivialCase(true); -/* for (int i = 1; i <= aMap.Extent(); i++) { - const TopoDS_Shape& aKey = aMap.FindKey(i); - //if (aKey.IsNotEqual(aSubShape)) continue; // find exactly the selected key - if (aKey.IsSame(aSubShape)) continue; - const TopTools_ListOfShape& anAncestors = aMap.FindFromIndex(i); - // check that it is not a trivial case (F1 & F2: aNumber = 1) - isTrivialCase = isTrivial(anAncestors, aSMap); - break; - }*/ - if(aMap.Contains(aSubShape)) { - const TopTools_ListOfShape& anAncestors = aMap.FindFromKey(aSubShape); - // check that it is not a trivial case (F1 & F2: aNumber = 1) - isTrivialCase = isTrivial(anAncestors, aSMap); - } else - break; - TopTools_ListOfShape aListOfNbs; - if(!isTrivialCase) { // find Neighbors - TNaming_Localizer aLocalizer; - TopTools_MapOfShape aMap3; - aLocalizer.FindNeighbourg(aContext, aSubShape, aMap3); - //int n = aMap3.Extent(); - TopTools_MapIteratorOfMapOfShape it(aMap3); - for(;it.More();it.Next()) { - const TopoDS_Shape& aNbShape = it.Key(); // neighbor edge - //TopAbs_ShapeEnum aType = aNbShape.ShapeType(); - const TopTools_ListOfShape& aList = aMap.FindFromKey(aNbShape); - TopTools_ListIteratorOfListOfShape it2(aList); - for(;it2.More();it2.Next()) { - if(aSMap.Contains(it2.Value())) continue; // skip this Face - aListOfNbs.Append(it2.Value()); - } - } - } // else a trivial case - - // build name of the sub-shape Edge - for(int i=1; i <= aSMap.Extent(); i++) { - const TopoDS_Shape& aFace = aSMap.FindKey(i); - std::string aFaceName = GetShapeName(aDoc, aFace, selectionLabel()); - if(i == 1) - aName = aFaceName; - else - aName += "|" + aFaceName; - } - TopTools_ListIteratorOfListOfShape itl(aListOfNbs); - for (;itl.More();itl.Next()) { - std::string aFaceName = GetShapeName(aDoc, itl.Value(), selectionLabel()); - aName += "|" + aFaceName; - } - } - break; - - case TopAbs_VERTEX: - // name structure (Monifold Topology): - // 1) F1 | F2 | F3 - intersection of 3 faces defines a vertex - trivial case. - // 2) F1 | F2 | F3 [|F4 [|Fn]] - redundant definition, but it should be kept as is to obtain safe recomputation - // 2) F1 | F2 - intersection of 2 faces definses a vertex - applicable for case - // when 1 faces is cylindrical, conical, spherical or revolution and etc. - // 3) E1 | E2 | E3 - intersection of 3 edges defines a vertex - when we have case of a shell - // or compound of 2 open faces. - // 4) E1 | E2 - intesection of 2 edges defines a vertex - when we have a case of - // two independent edges (wire or compound) - // implemented 2 first cases - { - TopTools_IndexedDataMapOfShapeListOfShape aMap; - TopExp::MapShapesAndAncestors(aContext, TopAbs_VERTEX, TopAbs_FACE, aMap); - const TopTools_ListOfShape& aList2 = aMap.FindFromKey(aSubShape); - TopTools_ListOfShape aList; - TopTools_MapOfShape aFMap; -#ifdef FIX_BUG1 - //int n = aList2.Extent(); //bug! duplication - // fix is below - TopTools_ListIteratorOfListOfShape itl2(aList2); - for (int i = 1;itl2.More();itl2.Next(),i++) { - if(aFMap.Add(itl2.Value())) - aList.Append(itl2.Value()); + return ModelAPI_AttributeSelection::NOT_CENTER; +} + +// type ::= COMP | COMS | SOLD | SHEL | FACE | WIRE | EDGE | VERT +void Model_AttributeSelection::selectSubShape( + const std::string& theType, const std::string& theSubShapeName) +{ + if(theSubShapeName.empty() || theType.empty()) return; + + std::string aSubShapeName = theSubShapeName; + CenterType aCenterType = theType[0] == 'v' || theType[0] == 'V' ? // only for vertex-type + centerTypeByName(aSubShapeName) : NOT_CENTER; + std::string aType = aCenterType == NOT_CENTER ? theType : "EDGE"; // search for edge now + + // first iteration is selection by name without center prefix, second - in case of problem, + // try with initial name + for(int aUseCenter = 1; aUseCenter >= 0; aUseCenter--) { + if (aUseCenter == 0 && aCenterType != NOT_CENTER) { + aSubShapeName = theSubShapeName; + aCenterType = NOT_CENTER; + aType = theType; + } else if (aUseCenter != 1) continue; + + // check this is Part-name: 2 delimiters in the name + std::size_t aPartEnd = aSubShapeName.find('/'); + if (aPartEnd != std::string::npos && aPartEnd != aSubShapeName.rfind('/')) { + std::string aPartName = aSubShapeName.substr(0, aPartEnd); + ObjectPtr aFound = owner()->document()->objectByName(ModelAPI_ResultPart::group(), aPartName); + if (aFound.get()) { // found such part, so asking it for the name + ResultPartPtr aPart = std::dynamic_pointer_cast(aFound); + std::string aNameInPart = aSubShapeName.substr(aPartEnd + 1); + int anIndex; + std::shared_ptr aSelected = aPart->shapeInPart(aNameInPart, aType, anIndex); + if (aSelected.get()) { + if (aCenterType != NOT_CENTER) { + if (!aSelected->isEdge()) + continue; + std::shared_ptr aSelectedEdge(new GeomAPI_Edge(aSelected)); + setValueCenter(aPart, aSelectedEdge, aCenterType); + } else + setValue(aPart, aSelected); + TDataStd_Integer::Set(selectionLabel(), anIndex); + return; } - //n = aList.Extent(); -#endif - int n = aList.Extent(); - if(n < 3) { // open topology case or Compound case => via edges - TopTools_IndexedDataMapOfShapeListOfShape aMap; - TopExp::MapShapesAndAncestors(aContext, TopAbs_VERTEX, TopAbs_EDGE, aMap); - const TopTools_ListOfShape& aList22 = aMap.FindFromKey(aSubShape); - if(aList22.Extent() >= 2) { // regular solution -#ifdef FIX_BUG1 - - // bug! duplication; fix is below - aFMap.Clear(); - TopTools_ListOfShape aListE; - TopTools_ListIteratorOfListOfShape itl2(aList22); - for (int i = 1;itl2.More();itl2.Next(),i++) { - if(aFMap.Add(itl2.Value())) - aListE.Append(itl2.Value()); + } + } + + Model_SelectionNaming aSelNaming(selectionLabel()); + std::shared_ptr aDoc = + std::dynamic_pointer_cast(owner()->document()); + std::shared_ptr aShapeToBeSelected; + ResultPtr aCont; + if (aSelNaming.selectSubShape(aType, aSubShapeName, aDoc, aShapeToBeSelected, aCont)) { + // try to find the last context to find the up to date shape + if (aCont->shape().get() && !aCont->shape()->isNull() && + aCont->groupName() == ModelAPI_ResultBody::group() && aDoc == owner()->document()) { + const TopoDS_Shape aConShape = aCont->shape()->impl(); + if (!aConShape.IsNull()) { + Handle(TNaming_NamedShape) aNS = TNaming_Tool::NamedShape(aConShape, selectionLabel()); + if (!aNS.IsNull()) { + aNS = TNaming_Tool::CurrentNamedShape(aNS); + if (!aNS.IsNull() && scope().Contains(aNS->Label())) { // scope check is for 2228 + TDF_Label aLab = aNS->Label(); + while(aLab.Depth() != 7 && aLab.Depth() > 5) + aLab = aLab.Father(); + ObjectPtr anObj = aDoc->objects()->object(aLab); + if (anObj.get()) { + ResultPtr aRes = std::dynamic_pointer_cast(anObj); + if (aRes) + aCont = aRes; + } } - n = aListE.Extent(); -#endif - TopTools_ListIteratorOfListOfShape itl(aListE); - for (int i = 1;itl.More();itl.Next(),i++) { - const TopoDS_Shape& anEdge = itl.Value(); - std::string anEdgeName = GetShapeName(aDoc, anEdge, selectionLabel()); - if(i == 1) - aName = anEdgeName; - else - aName += "|" + anEdgeName; + } + } + } + // if compsolid is context, try to take sub-solid as context: like in GUI and scripts + if (aCont.get() && aShapeToBeSelected.get()) { + ResultCompSolidPtr aComp = std::dynamic_pointer_cast(aCont); + if (aComp && aComp->numberOfSubs()) { + for(int aSubNum = 0; aSubNum < aComp->numberOfSubs(); aSubNum++) { + ResultPtr aSub = aComp->subResult(aSubNum); + if (aSub && aSub->shape().get() && aSub->shape()->isSubShape(aShapeToBeSelected)) { + aCont = aSub; + break; } - }//reg - else { // dangle vertex: if(aList22.Extent() == 1) - //it should be already in DF } - } - else { - TopTools_ListIteratorOfListOfShape itl(aList); - for (int i = 1;itl.More();itl.Next(),i++) { - const TopoDS_Shape& aFace = itl.Value(); - std::string aFaceName = GetShapeName(aDoc, aFace, selectionLabel()); - if(i == 1) - aName = aFaceName; - else - aName += "|" + aFaceName; + } + } + // try to find the latest active result that must be used instead of the selected + // to set the active context (like in GUI selection), not concealed one + bool aFindNewContext = true; + while(aFindNewContext && aCont.get()) { + aFindNewContext = false; + // take references to all results: root one, any sub + ResultCompSolidPtr aCompContext = ModelAPI_Tools::compSolidOwner(aCont); + int aSubsSize = (aCompContext.get() ? aCompContext->numberOfSubs() : 0) + 1; + for(int aResultNum = 0; aResultNum < aSubsSize; aResultNum++) { + ResultPtr aResCont = aCont; + if (aCompContext.get()) + if (aResultNum == aSubsSize - 1) + aResCont = aCompContext; + else aResCont = aCompContext->subResult(aResultNum); + const std::set& aRefs = aResCont->data()->refsToMe(); + std::set::const_iterator aRef = aRefs.begin(); + for(; !aFindNewContext && aRef != aRefs.end(); aRef++) { + if (!aRef->get() || !(*aRef)->owner().get()) + continue; + // concealed attribute only + FeaturePtr aRefFeat = std::dynamic_pointer_cast((*aRef)->owner()); + if (!ModelAPI_Session::get()->validators()->isConcealed( + aRefFeat->getKind(), (*aRef)->id())) + continue; + // search the feature result that contains sub-shape selected + std::list > aResults; + + // take all sub-results or one result + const std::list >& aFResults = aRefFeat->results(); + std::list >::const_iterator aRIter = aFResults.begin(); + for (; aRIter != aFResults.cend(); aRIter++) { + // iterate sub-bodies of compsolid + ResultCompSolidPtr aComp = + std::dynamic_pointer_cast(*aRIter); + if (aComp.get() && aComp->numberOfSubs() > 0) { + int aNumSub = aComp->numberOfSubs(); + for(int a = 0; a < aNumSub; a++) { + aResults.push_back(aComp->subResult(a)); + } + } else { + aResults.push_back(*aRIter); + } + } + std::list >::iterator aResIter = aResults.begin(); + for(; aResIter != aResults.end(); aResIter++) { + if (!aResIter->get() || !(*aResIter)->data()->isValid() || (*aResIter)->isDisabled()) + continue; + GeomShapePtr aShape = (*aResIter)->shape(); + GeomShapePtr aSelectedShape = + aShapeToBeSelected.get() ? aShapeToBeSelected : aCont->shape(); + if (aShape.get() && aShape->isSubShape(aSelectedShape, false)) { + aCont = *aResIter; // found new context (produced from this) with same subshape + //if (!aShape->isSubShape(aShapeToBeSelected, true)) // take context orientation + // aShapeToBeSelected->setOrientation(); + aFindNewContext = true; // continue searching futher + break; + } + } } } } - break; + + if (aCenterType != NOT_CENTER) { + if (!aShapeToBeSelected->isEdge()) + continue; + std::shared_ptr aSelectedEdge(new GeomAPI_Edge(aShapeToBeSelected)); + setValueCenter(aCont, aSelectedEdge, aCenterType); + } else + setValue(aCont, aShapeToBeSelected); + return; } - // register name - // aDoc->addNamingName(selectionLabel(), aName); - // the selected sub-shape will not be shared and as result it will not require registration } - return aName; -} -TopAbs_ShapeEnum translateType (const std::string& theType) -{ - // map from the textual shape types to OCCT enumeration - static std::map MyShapeTypes; - if (MyShapeTypes.size() == 0) { - MyShapeTypes["face"] = TopAbs_FACE; - MyShapeTypes["faces"] = TopAbs_FACE; - MyShapeTypes["vertex"] = TopAbs_VERTEX; - MyShapeTypes["vertices"] = TopAbs_VERTEX; - MyShapeTypes["wire"] = TopAbs_WIRE; - MyShapeTypes["edge"] = TopAbs_EDGE; - MyShapeTypes["edges"] = TopAbs_EDGE; - MyShapeTypes["shell"] = TopAbs_SHELL; - MyShapeTypes["solid"] = TopAbs_SOLID; - MyShapeTypes["solids"] = TopAbs_SOLID; - MyShapeTypes["FACE"] = TopAbs_FACE; - MyShapeTypes["FACES"] = TopAbs_FACE; - MyShapeTypes["VERTEX"] = TopAbs_VERTEX; - MyShapeTypes["VERTICES"] = TopAbs_VERTEX; - MyShapeTypes["WIRE"] = TopAbs_WIRE; - MyShapeTypes["EDGE"] = TopAbs_EDGE; - MyShapeTypes["EDGES"] = TopAbs_EDGE; - MyShapeTypes["SHELL"] = TopAbs_SHELL; - MyShapeTypes["SOLID"] = TopAbs_SOLID; - MyShapeTypes["SOLIDS"] = TopAbs_SOLID; - } - if (MyShapeTypes.find(theType) != MyShapeTypes.end()) - return MyShapeTypes[theType]; - Events_Error::send("Shape type defined in XML is not implemented!"); - return TopAbs_SHAPE; + TDF_Label aSelLab = selectionLabel(); + setInvalidIfFalse(aSelLab, false); + reset(); } -const TopoDS_Shape getShapeFromNS( - const std::string& theSubShapeName, Handle(TNaming_NamedShape) theNS) +int Model_AttributeSelection::Id() { - TopoDS_Shape aSelection; - std::string::size_type n = theSubShapeName.rfind('/'); - if (n == std::string::npos) n = 0; - std::string aSubString = theSubShapeName.substr(n + 1); - n = aSubString.rfind('_'); - if (n == std::string::npos) return aSelection; - aSubString = aSubString.substr(n+1); - int indx = atoi(aSubString.c_str()); - - TNaming_Iterator anItL(theNS); - for(int i = 1; anItL.More(); anItL.Next(), i++) { - if (i == indx) { - return anItL.NewShape(); + int anID = 0; + std::shared_ptr aSelection = value(); + std::shared_ptr aContext = context()->shape(); + // support for compsolids: + if (context().get() && ModelAPI_Tools::compSolidOwner(context()).get()) + aContext = ModelAPI_Tools::compSolidOwner(context())->shape(); + + + TopoDS_Shape aMainShape = aContext->impl(); + const TopoDS_Shape& aSubShape = aSelection->impl(); + // searching for the latest main shape + if (aSelection && !aSelection->isNull() && + aContext && !aContext->isNull()) + { + std::shared_ptr aDoc = + std::dynamic_pointer_cast(context()->document()); + if (aDoc.get()) { + Handle(TNaming_NamedShape) aNS = TNaming_Tool::NamedShape(aMainShape, aDoc->generalLabel()); + if (!aNS.IsNull()) { + aMainShape = TNaming_Tool::CurrentShape(aNS); + } } + + TopTools_IndexedMapOfShape aSubShapesMap; + TopExp::MapShapes(aMainShape, aSubShapesMap); + anID = aSubShapesMap.FindIndex(aSubShape); } - return aSelection; + return anID; } -const TopoDS_Shape findFaceByName( - const std::string& theSubShapeName, std::shared_ptr theDoc) +void Model_AttributeSelection::setId(int theID) { - TopoDS_Shape aFace; - std::string::size_type n, nb = theSubShapeName.rfind('/'); - if (nb == std::string::npos) nb = 0; - std::string aSubString = theSubShapeName.substr(nb + 1); - n = aSubString.rfind('_'); - if (n != std::string::npos) { - std::string aSubStr2 = aSubString.substr(0, n); - aSubString = theSubShapeName.substr(0, nb + 1); - aSubString = aSubString + aSubStr2; - } else - aSubString = theSubShapeName; - - const TDF_Label& aLabel = theDoc->findNamingName(aSubString); - if(aLabel.IsNull()) return aFace; - Handle(TNaming_NamedShape) aNS; - if(aLabel.FindAttribute(TNaming_NamedShape::GetID(), aNS)) { - aFace = getShapeFromNS(theSubShapeName, aNS); + const ResultPtr& aContext = context(); + std::shared_ptr aSelection; + + std::shared_ptr aContextShape = aContext->shape(); + // support for compsolids: + if (aContext.get() && ModelAPI_Tools::compSolidOwner(aContext).get()) + aContextShape = ModelAPI_Tools::compSolidOwner(aContext)->shape(); + + TopoDS_Shape aMainShape = aContextShape->impl(); + // searching for the latest main shape + if (theID > 0 && + aContextShape && !aContextShape->isNull()) + { + std::shared_ptr aDoc = + std::dynamic_pointer_cast(aContext->document()); + if (aDoc.get()) { + Handle(TNaming_NamedShape) aNS = TNaming_Tool::NamedShape(aMainShape, aDoc->generalLabel()); + if (!aNS.IsNull()) { + aMainShape = TNaming_Tool::CurrentShape(aNS); + } + } + + TopTools_IndexedMapOfShape aSubShapesMap; + TopExp::MapShapes(aMainShape, aSubShapesMap); + const TopoDS_Shape& aSelShape = aSubShapesMap.FindKey(theID); + + std::shared_ptr aResult(new GeomAPI_Shape); + aResult->setImpl(new TopoDS_Shape(aSelShape)); + + aSelection = aResult; } - return aFace; + + setValue(aContext, aSelection); } -int ParseName(const std::string& theSubShapeName, std::list& theList) +std::string Model_AttributeSelection::contextName(const ResultPtr& theContext) const { - std::string aName = theSubShapeName; - std::string aLastName; - int n1(0), n2(0); // n1 - start position, n2 - position of the delimiter - while ((n2 = aName.find('|', n1)) != std::string::npos) { - const std::string aName1 = aName.substr(n1, n2 - n1); //name of face - theList.push_back(aName1); - n1 = n2 + 1; - aLastName = aName.substr(n1); + std::string aResult; + if (owner()->document() != theContext->document()) { + if (theContext->document() == ModelAPI_Session::get()->moduleDocument()) { + aResult = theContext->document()->kind() + "/"; + } else { + ResultPtr aDocRes = ModelAPI_Tools::findPartResult( + ModelAPI_Session::get()->moduleDocument(), theContext->document()); + if (aDocRes.get()) { + aResult = aDocRes->data()->name() + "/"; + } + } } - if(!aLastName.empty()) - theList.push_back(aLastName); - return theList.size(); + aResult += theContext->data()->name(); + return aResult; } -const TopoDS_Shape findCommonShape( - const TopAbs_ShapeEnum theType, const TopTools_ListOfShape& theList) +void Model_AttributeSelection::computeValues( + ResultPtr theOldContext, ResultPtr theNewContext, TopoDS_Shape theValShape, + TopTools_ListOfShape& theShapes) { - TopoDS_Shape aShape; - std::vector aVec; - TopTools_MapOfShape aMap1, aMap2, aMap3, aMap4; - if(theList.Extent() > 1) { - aVec.push_back(aMap1); - aVec.push_back(aMap2); + bool aWasWholeContext = theValShape.IsNull(); + if (aWasWholeContext) { + //theShapes.Append(theValShape); + //return; + theValShape = theOldContext->shape()->impl(); } - if(theList.Extent() > 2) - aVec.push_back(aMap3); - if(theList.Extent() == 4) - aVec.push_back(aMap4); - - //fill maps - TopTools_ListIteratorOfListOfShape it(theList); - for(int i = 0;it.More();it.Next(),i++) { - const TopoDS_Shape& aFace = it.Value(); - if(i < 2) { - TopExp_Explorer anExp (aFace, theType); - for(;anExp.More();anExp.Next()) { - const TopoDS_Shape& anEdge = anExp.Current(); - if (!anEdge.IsNull()) - aVec[i].Add(anExp.Current()); - } - } else { - TopExp_Explorer anExp (aFace, TopAbs_VERTEX); - for(;anExp.More();anExp.Next()) { - const TopoDS_Shape& aVertex = anExp.Current(); - if (!aVertex.IsNull()) - aVec[i].Add(anExp.Current()); - } + //TopoDS_Shape anOldContShape = theOldContext->shape()->impl(); + TopoDS_Shape aNewContShape = theNewContext->shape()->impl(); + //if (anOldContShape.IsSame(theValShape)) { // full context shape substituted by new full context + //theShapes.Append(aNewContShape); + //return; + //} + // if a new value is unchanged in the new context, do nothing: value is correct + TopExp_Explorer aSubExp(aNewContShape, theValShape.ShapeType()); + for(; aSubExp.More(); aSubExp.Next()) { + if (aSubExp.Current().IsSame(theValShape)) { + theShapes.Append(theValShape); + return; } } - //trivial case: 2 faces - TopTools_ListOfShape aList; - TopTools_MapIteratorOfMapOfShape it2(aVec[0]); - for(;it2.More();it2.Next()) { - if(aVec[1].Contains(it2.Key())) { - aShape = it2.Key(); - if(theList.Extent() == 2) - break; - else - aList.Append(it2.Key()); - } + // if new context becomes compsolid, the resulting sub may be in sub-solids + std::list aNewToIterate; + aNewToIterate.push_back(theNewContext); + ResultCompSolidPtr aComp = std::dynamic_pointer_cast(theNewContext); + if (aComp.get()) { + for(int a = 0; a < aComp->numberOfSubs(); a++) + aNewToIterate.push_back(aComp->subResult(a, false)); } - if(aList.Extent() && aVec.size() > 3) {// list of common edges ==> search ny neighbors - if(aVec[2].Extent() && aVec[3].Extent()) { - TopTools_ListIteratorOfListOfShape it(aList); - for(;it.More();it.Next()) { - const TopoDS_Shape& aCand = it.Value(); - // not yet completelly implemented, to be rechecked - TopoDS_Vertex aV1, aV2; - TopExp::Vertices(TopoDS::Edge(aCand), aV1, aV2); - int aNum(0); - if(aVec[2].Contains(aV1)) aNum++; - else if(aVec[2].Contains(aV2)) aNum++; - if(aVec[3].Contains(aV1)) aNum++; - else if(aVec[3].Contains(aV2)) aNum++; - if(aNum == 2) { - aShape = aCand; - break; + + // first iteration: searching for the whole shape appearance (like face of the box) + // second iteration: searching for sub-shapes that contain the sub (like vertex on faces) + int aToFindPart = 0; + TopTools_DataMapOfShapeShape aNewToOld; // map from new containers to old containers (with val) + TopTools_MapOfShape anOlds; // to know how many olds produced new containers + for(; aToFindPart != 2 && theShapes.IsEmpty(); aToFindPart++) { + std::list::iterator aNewContIter = aNewToIterate.begin(); + for(; aNewContIter != aNewToIterate.end(); aNewContIter++) { + std::shared_ptr aNewData = + std::dynamic_pointer_cast((*aNewContIter)->data()); + TDF_Label aNewLab = aNewData->label(); + // searching for produced sub-shape fully on some label + TDF_ChildIDIterator aNSIter(aNewLab, TNaming_NamedShape::GetID(), Standard_True); + for(; aNSIter.More(); aNSIter.Next()) { + Handle(TNaming_NamedShape) aNS = Handle(TNaming_NamedShape)::DownCast(aNSIter.Value()); + for(TNaming_Iterator aPairIter(aNS); aPairIter.More(); aPairIter.Next()) { + if (aToFindPart == 0) { // search shape is fully inside + if (aPairIter.OldShape().IsSame(theValShape)) { + if (aPairIter.NewShape().IsNull()) {// value was removed + theShapes.Clear(); + return; + } + theShapes.Append(aPairIter.NewShape()); + } + } else if (!aPairIter.OldShape().IsNull()) { // search shape that contains this sub + TopExp_Explorer anExp(aPairIter.OldShape(), theValShape.ShapeType()); + for(; anExp.More(); anExp.Next()) { + if (anExp.Current().IsSame(theValShape)) { // found a new container + if (aPairIter.NewShape().IsNull()) {// value was removed + theShapes.Clear(); + return; + } + aNewToOld.Bind(aPairIter.NewShape(), aPairIter.OldShape()); + anOlds.Add(aPairIter.OldShape()); + break; + } + } + } } } } } - - if(aList.Extent() && aVec.size() == 3) { - - TopTools_ListIteratorOfListOfShape it(aList); - for(;it.More();it.Next()) { - const TopoDS_Shape& aCand = it.Value(); - if(aVec[2].Contains(aCand)) { - aShape = aCand; - break; + if (aToFindPart == 2 && !aNewToOld.IsEmpty()) { + // map of sub-shapes -> number of occurences of these shapes in containers + NCollection_DataMap aSubs; + TopTools_DataMapOfShapeShape::Iterator aContIter(aNewToOld); + for(; aContIter.More(); aContIter.Next()) { + TopExp_Explorer aSubExp(aContIter.Key(), theValShape.ShapeType()); + for(; aSubExp.More(); aSubExp.Next()) { + if (!aSubs.IsBound(aSubExp.Current())) { + aSubs.Bind(aSubExp.Current(), TopTools_MapOfShape()); + } + // store old to know how many olds produced this shape + aSubs.ChangeFind(aSubExp.Current()).Add(aContIter.Value()); } } + // if sub is appeared same times in containers as the number of old shapes that contain it + int aCountInOld = anOlds.Size(); + NCollection_DataMap::Iterator + aSubsIter(aSubs); + for(; aSubsIter.More(); aSubsIter.Next()) { + if (aSubsIter.Value().Size() == aCountInOld) { + theShapes.Append(aSubsIter.Key()); + } + } + } + if (theShapes.IsEmpty()) { // nothing was changed + theShapes.Append(aWasWholeContext ? TopoDS_Shape() : theValShape); } - return aShape; } -std::string getContextName(const std::string& theSubShapeName) +bool Model_AttributeSelection::searchNewContext(std::shared_ptr theDoc, + const TopoDS_Shape theContShape, ResultPtr theContext, TopoDS_Shape theValShape, + TDF_Label theAccessLabel, + std::list& theResults, TopTools_ListOfShape& theValShapes) { - std::string aName; - std::string::size_type n = theSubShapeName.find('/'); - if (n == std::string::npos) return aName; - aName = theSubShapeName.substr(0, n); - return aName; -} -// type ::= COMP | COMS | SOLD | SHEL | FACE | WIRE | EDGE | VERT -void Model_AttributeSelection::selectSubShape( - const std::string& theType, const std::string& theSubShapeName) -{ - if(theSubShapeName.empty() || theType.empty()) return; - TopAbs_ShapeEnum aType = translateType(theType); - std::shared_ptr aDoc = - std::dynamic_pointer_cast(owner()->document()); - std::string aContName = getContextName(theSubShapeName); - if(aContName.empty()) return; - //ResultPtr aCont = context(); - ResultPtr aCont = aDoc->findByName(aContName); - if(!aCont.get() || aCont->shape()->isNull()) return; - TopoDS_Shape aContext = aCont->shape()->impl(); - TopAbs_ShapeEnum aContType = aContext.ShapeType(); - if(aType <= aContType) return; // not applicable - - - TopoDS_Shape aSelection; - switch (aType) - { - case TopAbs_COMPOUND: - break; - case TopAbs_COMPSOLID: - break; - case TopAbs_SOLID: - break; - case TopAbs_SHELL: - break; - case TopAbs_FACE: - { - const TopoDS_Shape aSelection = findFaceByName(theSubShapeName, aDoc); - if(!aSelection.IsNull()) {// Select it - std::shared_ptr aShapeToBeSelected(new GeomAPI_Shape()); - aShapeToBeSelected->setImpl(new TopoDS_Shape(aSelection)); - setValue(aCont, aShapeToBeSelected); + std::set aResults; // to avoid duplicates, new context, null if deleted + TopTools_ListOfShape aResContShapes; + // iterate context and shape, but also if it is sub-shape of main shape, check also it + TopTools_ListOfShape aContextList; + aContextList.Append(theContShape); + if (theContext.get()) { + ResultPtr aComposite = ModelAPI_Tools::compSolidOwner(theContext); + if (aComposite.get() && aComposite->shape().get() && !aComposite->shape()->isNull()) + aContextList.Append(aComposite->shape()->impl()); + } + for(TopTools_ListOfShape::Iterator aContIter(aContextList); aContIter.More(); aContIter.Next()) { + TNaming_SameShapeIterator aModifIter(aContIter.ChangeValue(), theAccessLabel); + for(; aModifIter.More(); aModifIter.Next()) { + TDF_Label anObjLab = aModifIter.Label().Father(); + ResultPtr aModifierObj = std::dynamic_pointer_cast + (theDoc->objects()->object(anObjLab)); + if (!aModifierObj.get()) { + // #2241: shape may be sub-element of new object, not main (shell created from faces) + if (!anObjLab.IsRoot()) + aModifierObj = std::dynamic_pointer_cast + (theDoc->objects()->object(anObjLab.Father())); + if (!aModifierObj.get()) + continue; } - } - break; - case TopAbs_WIRE: - break; - case TopAbs_EDGE: - { - TopoDS_Shape aSelection;// = findFaceByName(theSubShapeName, aDoc); - const TDF_Label& aLabel = aDoc->findNamingName(theSubShapeName); - if(!aLabel.IsNull()) { - Handle(TNaming_NamedShape) aNS; - if(aLabel.FindAttribute(TNaming_NamedShape::GetID(), aNS)) { - aSelection = getShapeFromNS(theSubShapeName, aNS); - } + FeaturePtr aModifierFeat = theDoc->feature(aModifierObj); + if (!aModifierFeat.get()) + continue; + FeaturePtr aThisFeature = std::dynamic_pointer_cast(owner()); + if (aModifierFeat == aThisFeature || theDoc->objects()->isLater(aModifierFeat, aThisFeature)) + continue; // the modifier feature is later than this, so, should not be used + FeaturePtr aCurrentModifierFeat = theDoc->feature(theContext); + if (aCurrentModifierFeat == aModifierFeat || + theDoc->objects()->isLater(aCurrentModifierFeat, aModifierFeat)) + continue; // the current modifier is later than the found, so, useless + Handle(TNaming_NamedShape) aNewNS; + aModifIter.Label().FindAttribute(TNaming_NamedShape::GetID(), aNewNS); + if (aNewNS->Evolution() == TNaming_MODIFY || aNewNS->Evolution() == TNaming_GENERATED) { + aResults.insert(aModifierObj); + //TNaming_Iterator aPairIter(aNewNS); + //aResContShapes.Append(aPairIter.NewShape()); + aResContShapes.Append(aModifierObj->shape()->impl()); + } else if (aNewNS->Evolution() == TNaming_DELETE) { // a shape was deleted => result is empty + aResults.insert(ResultPtr()); + } else { // not-precessed modification => don't support it + continue; } - if(aSelection.IsNull()) { - std::list aListofNames; - int n = ParseName(theSubShapeName, aListofNames); - if(n > 1 && n < 5) { - TopTools_ListOfShape aList; - std::list::iterator it =aListofNames.begin(); - for(;it != aListofNames.end();it++){ - const TopoDS_Shape aFace = findFaceByName(*it, aDoc); - aList.Append(aFace); + } + } + if (aResults.empty()) + return false; // no modifications found, must stay the same + // iterate all results to find futher modifications + std::set::iterator aResIter = aResults.begin(); + for(; aResIter != aResults.end(); aResIter++) { + if (aResIter->get() != NULL) { + // compute new values by two contextes: the old and the new + TopTools_ListOfShape aValShapes; + computeValues(theContext, *aResIter, theValShape, aValShapes); + + TopTools_ListIteratorOfListOfShape aNewVal(aValShapes); + for(; aNewVal.More(); aNewVal.Next()) { + std::list aNewRes; + TopTools_ListOfShape aNewUpdatedVal; + TopoDS_Shape aNewValSh = aNewVal.Value(); + TopoDS_Shape aNewContShape = (*aResIter)->shape()->impl(); + if (theValShape.IsNull() && aNewContShape.IsSame(aNewValSh)) + aNewValSh.Nullify(); + if (searchNewContext(theDoc, aNewContShape, *aResIter, aNewValSh, + theAccessLabel, aNewRes, aNewUpdatedVal)) + { + // appeand new results instead of the current ones + std::list::iterator aNewIter = aNewRes.begin(); + TopTools_ListIteratorOfListOfShape aNewUpdVal(aNewUpdatedVal); + for(; aNewIter != aNewRes.end(); aNewIter++, aNewUpdVal.Next()) { + theResults.push_back(*aNewIter); + theValShapes.Append(aNewUpdVal.Value()); } - aSelection = findCommonShape(TopAbs_EDGE, aList); + } else { // the current result is good + theResults.push_back(*aResIter); + theValShapes.Append(aNewValSh); } } - if(!aSelection.IsNull()) {// Select it - std::shared_ptr aShapeToBeSelected(new GeomAPI_Shape()); - aShapeToBeSelected->setImpl(new TopoDS_Shape(aSelection)); - setValue(aCont, aShapeToBeSelected); - } } - break; - case TopAbs_VERTEX: - { - TopoDS_Shape aSelection; - const TDF_Label& aLabel = aDoc->findNamingName(theSubShapeName); - if(!aLabel.IsNull()) { - Handle(TNaming_NamedShape) aNS; - if(aLabel.FindAttribute(TNaming_NamedShape::GetID(), aNS)) { - aSelection = getShapeFromNS(theSubShapeName, aNS); - } - } - if(aSelection.IsNull()) { - std::list aListofNames; - int n = ParseName(theSubShapeName, aListofNames); - if(n > 1 && n < 4) { // 2 || 3 - TopTools_ListOfShape aList; - std::list::iterator it = aListofNames.begin(); - for(; it != aListofNames.end(); it++){ - const TopoDS_Shape aFace = findFaceByName(*it, aDoc); - if(!aFace.IsNull()) - aList.Append(aFace); + } + return true; // theResults must be empty: everything is deleted +} + +void Model_AttributeSelection::updateInHistory() +{ + ResultPtr aContext = std::dynamic_pointer_cast(myRef.value()); + // only bodies and parts may be modified later in the history, don't do anything otherwise + if (!aContext.get() || (aContext->groupName() != ModelAPI_ResultBody::group() && + aContext->groupName() != ModelAPI_ResultPart::group())) + return; + std::shared_ptr aDoc = + std::dynamic_pointer_cast(aContext->document()); + std::shared_ptr aContData = std::dynamic_pointer_cast(aContext->data()); + if (!aContData.get() || !aContData->isValid()) + return; + TDF_Label aContLab = aContData->label(); // named shape where the selected context is located + Handle(TNaming_NamedShape) aContNS; + if (!aContLab.FindAttribute(TNaming_NamedShape::GetID(), aContNS)) { + bool aFoundNewContext = true; + ResultPtr aNewContext = aContext; + while(aFoundNewContext) { + aFoundNewContext = false; + // parts have no shape in result, so, trace references using the Part info + if (aNewContext->groupName() == ModelAPI_ResultPart::group()) { + ResultPartPtr aPartContext = std::dynamic_pointer_cast(aNewContext); + if (aPartContext.get()) { // searching for the up to date references to the referenced cont + const std::set& aRefs = aPartContext->data()->refsToMe(); + std::set::const_iterator aRef = aRefs.begin(); + for(; aRef != aRefs.end(); aRef++) { + // to avoid detection of part changes by local selection only + AttributeSelectionPtr aSel = + std::dynamic_pointer_cast(*aRef); + if (aSel.get() && !aSel->value()->isSame(aSel->context()->shape())) + continue; + + FeaturePtr aRefFeat = std::dynamic_pointer_cast((*aRef)->owner()); + if (aRefFeat.get() && aRefFeat != owner()) { + FeaturePtr aThisFeature = std::dynamic_pointer_cast(owner()); + if (aDoc->objects()->isLater(aThisFeature, aRefFeat)) { // found better feature + aFoundNewContext = true; + aNewContext = aRefFeat->firstResult(); + } + } } - aSelection = findCommonShape(TopAbs_VERTEX, aList); } } - if(!aSelection.IsNull()) {// Select it - std::shared_ptr aShapeToBeSelected(new GeomAPI_Shape()); - aShapeToBeSelected->setImpl(new TopoDS_Shape(aSelection)); - setValue(aCont, aShapeToBeSelected); - } } - break; - default: //TopAbs_SHAPE + if (aNewContext != aContext) { + setValue(aNewContext, value()); + } + return; + } + FeaturePtr aThisFeature = std::dynamic_pointer_cast(owner()); + FeaturePtr aCurrentModifierFeat = aDoc->feature(aContext); + // iterate the context shape modifications in order to find a feature that is upper in history + // that this one and is really modifies the referenced result to refer to it + ResultPtr aModifierResFound; + TNaming_Iterator aPairIter(aContNS); + if (!aPairIter.More()) return; + TopoDS_Shape aNewCShape = aPairIter.NewShape(); + bool anIterate = true; + // trying to update also the sub-shape selected + GeomShapePtr aSubShape = value(); + if (aSubShape.get() && aSubShape->isEqual(aContext->shape())) + aSubShape.reset(); + TopoDS_Shape aValShape; + if (aSubShape.get()) { + aValShape = aSubShape->impl(); } + std::list aNewContexts; + TopTools_ListOfShape aValShapes; + if (searchNewContext(aDoc, aNewCShape, aContext, aValShape, aContLab, aNewContexts, aValShapes)) + { + // update scope to reset to a new one + myScope.Clear(); + + std::list::iterator aNewCont = aNewContexts.begin(); + TopTools_ListIteratorOfListOfShape aNewValues(aValShapes); + if (aNewCont == aNewContexts.end()) { // all results were deleted + ResultPtr anEmptyContext; + std::shared_ptr anEmptyShape; + setValue(anEmptyContext, anEmptyShape); // nullify the selection + return; + } + + GeomShapePtr aValueShape; + if (!aNewValues.Value().IsNull()) { + aValueShape = std::make_shared(); + aValueShape->setImpl(new TopoDS_Shape(aNewValues.Value())); + } + setValue(*aNewCont, aValueShape); + // if there are more than one result, put them by "append" into "parent" list + if (myParent) { + for(aNewCont++, aNewValues.Next(); aNewCont != aNewContexts.end(); + aNewCont++, aNewValues.Next()) { + GeomShapePtr aValueShape; + if (!aNewValues.Value().IsNull()) { + aValueShape = std::make_shared(); + aValueShape->setImpl(new TopoDS_Shape(aNewValues.Value())); + } + myParent->append(*aNewCont, aValueShape); + } + } + } } -int Model_AttributeSelection::Id() +void Model_AttributeSelection::setParent(Model_AttributeSelectionList* theParent) { - std::shared_ptr aSelection = value(); - std::shared_ptr aContext = context()->shape(); - const TopoDS_Shape& aMainShape = aContext->impl(); - const TopoDS_Shape& aSubShape = aSelection->impl(); - int anID = 0; - if (aSelection && !aSelection->isNull() && - aContext && !aContext->isNull()) - { - TopTools_IndexedMapOfShape aSubShapesMap; - TopExp::MapShapes(aMainShape, aSubShapesMap); - anID = aSubShapesMap.FindIndex(aSubShape); - } - return anID; + myParent = theParent; }