From 64e62ba3761ad21ec49f1c13a64133567a0fa10f Mon Sep 17 00:00:00 2001 From: azv Date: Tue, 28 Jan 2020 15:55:44 +0300 Subject: [PATCH] Implement possibility to add a pole to B-spline --- src/GeomData/GeomData_Point2DArray.cpp | 4 +- src/PartSet/PartSet_BSplineWidget.cpp | 6 +- src/SketchPlugin/SketchPlugin_BSplineBase.cpp | 135 ++++++++++++++++++ src/SketchPlugin/SketchPlugin_BSplineBase.h | 15 ++ .../SketchPlugin_MacroBSpline.cpp | 45 +++--- src/SketchPlugin/SketchPlugin_MacroBSpline.h | 12 ++ .../PlaneGCSSolver_AttributeBuilder.cpp | 13 +- .../SketchSolver_ConstraintCoincidence.cpp | 15 ++ .../SketchSolver_ConstraintCoincidence.h | 4 + 9 files changed, 219 insertions(+), 30 deletions(-) diff --git a/src/GeomData/GeomData_Point2DArray.cpp b/src/GeomData/GeomData_Point2DArray.cpp index 4f18c7f76..198f91605 100644 --- a/src/GeomData/GeomData_Point2DArray.cpp +++ b/src/GeomData/GeomData_Point2DArray.cpp @@ -77,8 +77,10 @@ void GeomData_Point2DArray::setSize(const int theSize) } else { // reset the old array if (aValuesSize) { - if (aValuesSize != myArray->Length()) { // old data is not keept, a new array is created + if (aValuesSize != myArray->Length()) { // old data is kept in the new array Handle(TColStd_HArray1OfReal) aNewArray = new TColStd_HArray1OfReal(0, aValuesSize - 1); + for (int anIndex = 0; anIndex < aValuesSize && anIndex <= myArray->Upper(); ++anIndex) + aNewArray->SetValue(anIndex, myArray->Value(anIndex)); myArray->ChangeArray(aNewArray); owner()->data()->sendAttributeUpdated(this); } diff --git a/src/PartSet/PartSet_BSplineWidget.cpp b/src/PartSet/PartSet_BSplineWidget.cpp index 43632faec..9ca97448d 100644 --- a/src/PartSet/PartSet_BSplineWidget.cpp +++ b/src/PartSet/PartSet_BSplineWidget.cpp @@ -206,7 +206,11 @@ void PartSet_BSplineWidget::onAddPole() } } if (aFound) { - // TODO: add a new pole after found Id + // add a new pole after found Id + std::ostringstream anActionName; + anActionName << SketchPlugin_BSplineBase::ADD_POLE_ACTION_ID() << "#" << aId; + if (feature()->customAction(anActionName.str())) + updateObject(feature()); restoreValueCustom(); } diff --git a/src/SketchPlugin/SketchPlugin_BSplineBase.cpp b/src/SketchPlugin/SketchPlugin_BSplineBase.cpp index e39dfe6d9..380887a64 100644 --- a/src/SketchPlugin/SketchPlugin_BSplineBase.cpp +++ b/src/SketchPlugin/SketchPlugin_BSplineBase.cpp @@ -18,15 +18,23 @@ // #include + +#include +#include +#include #include +#include + #include +#include #include #include #include +#include #include #include #include @@ -150,3 +158,130 @@ void SketchPlugin_BSplineBase::attributeChanged(const std::string& theID) { //// } } } + +bool SketchPlugin_BSplineBase::customAction(const std::string& theActionId) +{ + // parse for the action and an index divided by '#' + std::string anAction; + int anIndex = -1; + size_t pos = theActionId.find('#'); + if (pos != std::string::npos) { + anAction = theActionId.substr(0, pos); + anIndex = std::stoi(theActionId.substr(pos + 1)); + } + + if (anAction == ADD_POLE_ACTION_ID()) { + return addPole(anIndex); + } + + std::string aMsg = "Error: Feature \"%1\" does not support action \"%2\"."; + Events_InfoMessage("SketchPlugin_BSplineBase", aMsg).arg(getKind()).arg(anAction).send(); + return false; +} + +bool SketchPlugin_BSplineBase::addPole(const int theAfter) +{ + AttributePoint2DArrayPtr aPolesArray = + std::dynamic_pointer_cast(attribute(POLES_ID())); + AttributeDoubleArrayPtr aWeightsArray = data()->realArray(WEIGHTS_ID()); + + int anAfter = (!isPeriodic() && theAfter == aPolesArray->size() - 1) ? theAfter - 1 : theAfter; + + // find internal coincidences applied to the poles with greater indices + std::list aCoincidentPoleIndex; + bool hasAuxSegment = false; + const std::set& aRefs = data()->refsToMe(); + for (std::set::iterator anIt = aRefs.begin(); anIt != aRefs.end(); ++anIt) { + FeaturePtr aFeature = ModelAPI_Feature::feature((*anIt)->owner()); + if (aFeature->getKind() == SketchPlugin_ConstraintCoincidenceInternal::ID()) { + AttributeIntegerPtr anIndex; + if ((*anIt)->id() == SketchPlugin_ConstraintCoincidenceInternal::ENTITY_A()) + anIndex = aFeature->integer(SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_A()); + else if ((*anIt)->id() == SketchPlugin_ConstraintCoincidenceInternal::ENTITY_B()) + anIndex = aFeature->integer(SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B()); + + if (anIndex && anIndex->isInitialized()) { + if (anIndex->value() > anAfter) + aCoincidentPoleIndex.push_back(anIndex); + else if (anIndex->value() == anAfter && !hasAuxSegment) { + // check the constrained object is a segment of the control polygon + const std::string& anOtherAttr = + (*anIt)->id() == SketchPlugin_ConstraintCoincidenceInternal::ENTITY_A() ? + SketchPlugin_ConstraintCoincidenceInternal::ENTITY_B() : + SketchPlugin_ConstraintCoincidenceInternal::ENTITY_A(); + AttributeRefAttrPtr aRefAttr = aFeature->refattr(anOtherAttr); + if (aRefAttr && !aRefAttr->isObject() && + aRefAttr->attr()->id() == SketchPlugin_Line::START_ID()) { + hasAuxSegment = true; + aCoincidentPoleIndex.push_back(anIndex); + } + } + } + } + } + + bool aWasBlocked = data()->blockSendAttributeUpdated(true); + + // add new pole and default weight + std::list aPoles; + aPolesArray->setSize(aPolesArray->size() + 1); + aPolesArray->setPnt(aPolesArray->size() - 1, aPolesArray->pnt(0)); // for periodic spline + for (int i = aPolesArray->size() - 2; i > anAfter; --i) { + aPoles.push_front(aPolesArray->pnt(i)); + aPolesArray->setPnt(i + 1, aPoles.front()); + } + + GeomPnt2dPtr aCurPole = aPolesArray->pnt(anAfter); + GeomPnt2dPtr aNextPole = aPolesArray->pnt(anAfter + 1); + aPolesArray->setPnt(anAfter + 1, (aCurPole->x() + aNextPole->x()) * 0.5, + (aCurPole->y() + aNextPole->y()) * 0.5); + for (int i = anAfter + 1; i >= 0; --i) + aPoles.push_front(aPolesArray->pnt(i)); + + std::list aWeights; + for (int i = 0; i < aWeightsArray->size(); ++i) { + aWeights.push_back(aWeightsArray->value(i)); + if (i == anAfter) + aWeights.push_back(1.0); // default weight + } + aWeightsArray->setSize(aWeightsArray->size() + 1); + std::list::iterator aWIt = aWeights.begin(); + for (int i = 0; i < aWeightsArray->size(); ++i, ++aWIt) + aWeightsArray->setValue(i, *aWIt); + + // recalculate knots and multiplicities + std::shared_ptr aBSplineCurve( + new GeomAPI_BSpline2d(aPoles, aWeights, isPeriodic())); + + integer(DEGREE_ID())->setValue(aBSplineCurve->degree()); + + AttributeDoubleArrayPtr aKnotsAttr = data()->realArray(SketchPlugin_BSplineBase::KNOTS_ID()); + std::list aKnots = aBSplineCurve->knots(); + int aSize = (int)aKnots.size(); + aKnotsAttr->setSize(aSize); + std::list::iterator aKIt = aKnots.begin(); + for (int index = 0; index < aSize; ++index, ++aKIt) + aKnotsAttr->setValue(index, *aKIt); + + AttributeIntArrayPtr aMultsAttr = data()->intArray(SketchPlugin_BSplineBase::MULTS_ID()); + std::list aMults = aBSplineCurve->mults(); + aSize = (int)aMults.size(); + aMultsAttr->setSize(aSize); + std::list::iterator aMIt = aMults.begin(); + for (int index = 0; index < aSize; ++index, ++aMIt) + aMultsAttr->setValue(index, *aMIt); + + data()->blockSendAttributeUpdated(aWasBlocked, true); + + // update indices of internal coincidences + for (std::list::iterator aCIt = aCoincidentPoleIndex.begin(); + aCIt != aCoincidentPoleIndex.end(); ++aCIt) + (*aCIt)->setValue((*aCIt)->value() + 1); + + // create auxiliary segment and pole updating the control polygon + SketchPlugin_MacroBSpline::createAuxiliaryPole(aPolesArray, anAfter + 1); + if (hasAuxSegment) + SketchPlugin_MacroBSpline::createAuxiliarySegment(aPolesArray, anAfter, anAfter + 1); + + return true; +} diff --git a/src/SketchPlugin/SketchPlugin_BSplineBase.h b/src/SketchPlugin/SketchPlugin_BSplineBase.h index d84a17a9c..fc75396af 100644 --- a/src/SketchPlugin/SketchPlugin_BSplineBase.h +++ b/src/SketchPlugin/SketchPlugin_BSplineBase.h @@ -65,6 +65,13 @@ public: return ID; } + /// name for add pole action + inline static const std::string& ADD_POLE_ACTION_ID() + { + static const std::string ID("AddPole"); + return ID; + } + /// Returns true is sketch element is under the rigid constraint SKETCHPLUGIN_EXPORT virtual bool isFixed(); @@ -74,6 +81,11 @@ public: /// Creates a new part document if needed SKETCHPLUGIN_EXPORT virtual void execute(); + /// Updates the B-spline curve. + /// \param[in] theActionId action key id (in following form: Action#Index) + /// \return \c false in case the action not performed. + SKETCHPLUGIN_EXPORT virtual bool customAction(const std::string& theActionId); + protected: /// Called from the derived class SketchPlugin_BSplineBase(); @@ -83,6 +95,9 @@ protected: /// \brief Return \c true if the B-spline curve is periodic virtual bool isPeriodic() const = 0; + + /// Add new pole after the pole with the given index + bool addPole(const int theAfter); }; #endif diff --git a/src/SketchPlugin/SketchPlugin_MacroBSpline.cpp b/src/SketchPlugin/SketchPlugin_MacroBSpline.cpp index a7591e3c5..4f491fe2d 100644 --- a/src/SketchPlugin/SketchPlugin_MacroBSpline.cpp +++ b/src/SketchPlugin/SketchPlugin_MacroBSpline.cpp @@ -45,16 +45,7 @@ #include -// Create Point feature coincident with the B-spline pole -static FeaturePtr createAuxiliaryPole(FeaturePtr theBSpline, - AttributePoint2DArrayPtr theBSplinePoles, - const int thePoleIndex); -// Create segment between consequtive B-spline poles -static void createAuxiliarySegment(FeaturePtr theBSpline, - AttributePoint2DArrayPtr theBSplinePoles, - const int thePoleIndex1, - const int thePoleIndex2); -// Create internal coincidence constraint with B-spline pole +/// Create internal coincidence constraint with B-spline pole static void createInternalConstraint(SketchPlugin_Sketch* theSketch, AttributePtr thePoint, AttributePtr theBSplinePoles, @@ -211,13 +202,13 @@ void SketchPlugin_MacroBSpline::createControlPolygon(FeaturePtr theBSpline, int aSize = aPoles->size(); // poles for (int index = 0; index < aSize; ++index) - thePoles.push_back(createAuxiliaryPole(theBSpline, aPoles, index)); + thePoles.push_back(createAuxiliaryPole(aPoles, index)); // segments for (int index = 1; index < aSize; ++index) - createAuxiliarySegment(theBSpline, aPoles, index - 1, index); + createAuxiliarySegment(aPoles, index - 1, index); if (myIsPeriodic) { // additional segment to close the control polygon - createAuxiliarySegment(theBSpline, aPoles, aSize - 1, 0); + createAuxiliarySegment(aPoles, aSize - 1, 0); } } @@ -315,17 +306,18 @@ AISObjectPtr SketchPlugin_MacroBSpline::getAISObject(AISObjectPtr thePrevious) // ========================== Auxiliary functions =========================================== -FeaturePtr createAuxiliaryPole(FeaturePtr theBSpline, - AttributePoint2DArrayPtr theBSplinePoles, - const int thePoleIndex) +FeaturePtr SketchPlugin_MacroBSpline::createAuxiliaryPole(AttributePoint2DArrayPtr theBSplinePoles, + const int thePoleIndex) { + FeaturePtr aBSpline = ModelAPI_Feature::feature(theBSplinePoles->owner()); + SketchPlugin_Sketch* aSketch = - std::dynamic_pointer_cast(theBSpline)->sketch(); + std::dynamic_pointer_cast(aBSpline)->sketch(); // create child point equal to the B-spline's pole FeaturePtr aPointFeature = aSketch->addFeature(SketchPlugin_Point::ID()); aPointFeature->boolean(SketchPlugin_Point::AUXILIARY_ID())->setValue(true); - aPointFeature->reference(SketchPlugin_Point::PARENT_ID())->setValue(theBSpline); + aPointFeature->reference(SketchPlugin_Point::PARENT_ID())->setValue(aBSpline); GeomPnt2dPtr aPole = theBSplinePoles->pnt(thePoleIndex); @@ -336,7 +328,7 @@ FeaturePtr createAuxiliaryPole(FeaturePtr theBSpline, aPointFeature->execute(); std::ostringstream aName; - aName << theBSpline->name() << "_" << theBSplinePoles->id() << "_" << thePoleIndex; + aName << aBSpline->name() << "_" << theBSplinePoles->id() << "_" << thePoleIndex; aPointFeature->data()->setName(aName.str()); aPointFeature->lastResult()->data()->setName(aName.str()); @@ -346,18 +338,19 @@ FeaturePtr createAuxiliaryPole(FeaturePtr theBSpline, return aPointFeature; } -void createAuxiliarySegment(FeaturePtr theBSpline, - AttributePoint2DArrayPtr theBSplinePoles, - const int thePoleIndex1, - const int thePoleIndex2) +void SketchPlugin_MacroBSpline::createAuxiliarySegment(AttributePoint2DArrayPtr theBSplinePoles, + const int thePoleIndex1, + const int thePoleIndex2) { + FeaturePtr aBSpline = ModelAPI_Feature::feature(theBSplinePoles->owner()); + SketchPlugin_Sketch* aSketch = - std::dynamic_pointer_cast(theBSpline)->sketch(); + std::dynamic_pointer_cast(aBSpline)->sketch(); // create child segment between B-spline poles FeaturePtr aLineFeature = aSketch->addFeature(SketchPlugin_Line::ID()); aLineFeature->boolean(SketchPlugin_Point::AUXILIARY_ID())->setValue(true); - aLineFeature->reference(SketchPlugin_Point::PARENT_ID())->setValue(theBSpline); + aLineFeature->reference(SketchPlugin_Point::PARENT_ID())->setValue(aBSpline); AttributePoint2DPtr aLineStart = std::dynamic_pointer_cast( aLineFeature->attribute(SketchPlugin_Line::START_ID())); @@ -370,7 +363,7 @@ void createAuxiliarySegment(FeaturePtr theBSpline, aLineFeature->execute(); std::ostringstream aName; - aName << theBSpline->name() << "_segment_" << thePoleIndex1 << "_" << thePoleIndex2; + aName << aBSpline->name() << "_segment_" << thePoleIndex1 << "_" << thePoleIndex2; aLineFeature->data()->setName(aName.str()); aLineFeature->lastResult()->data()->setName(aName.str()); diff --git a/src/SketchPlugin/SketchPlugin_MacroBSpline.h b/src/SketchPlugin/SketchPlugin_MacroBSpline.h index 78ef86823..860c34f9d 100644 --- a/src/SketchPlugin/SketchPlugin_MacroBSpline.h +++ b/src/SketchPlugin/SketchPlugin_MacroBSpline.h @@ -31,6 +31,8 @@ class GeomAPI_Circ2d; class GeomAPI_Pnt2d; +class GeomDataAPI_Point2DArray; + /**\class SketchPlugin_MacroBSpline * \ingroup Plugins * \brief Feature for creation of the new B-spline in Sketch. @@ -114,6 +116,16 @@ private: void createControlPolygon(FeaturePtr theBSpline, std::list& thePoles); void constraintsForPoles(const std::list& thePoles); + /// Create Point feature coincident with the B-spline pole + static FeaturePtr createAuxiliaryPole(std::shared_ptr theBSplinePoles, + const int thePoleIndex); + /// Create segment between consequtive B-spline poles + static void createAuxiliarySegment(std::shared_ptr theBSplinePoles, + const int thePoleIndex1, + const int thePoleIndex2); + friend class SketchPlugin_BSplineBase; + +private: std::list myKnots; std::list myMultiplicities; int myDegree; diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_AttributeBuilder.cpp b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_AttributeBuilder.cpp index 3323048d4..a446abc66 100644 --- a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_AttributeBuilder.cpp +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_AttributeBuilder.cpp @@ -218,9 +218,18 @@ bool PlaneGCSSolver_AttributeBuilder::updateAttribute( if (aWrapper->size() != anAttribute->size()) { std::vector aPointsArray = aWrapper->array(); + std::vector::iterator aPos = aPointsArray.begin(); while (anAttribute->size() > (int)aPointsArray.size()) { // add points to the middle of array - aPointsArray.insert(--aPointsArray.end(), createPoint(GeomPnt2dPtr(), myStorage)); + GeomPnt2dPtr aValue; + for (; aPos != aPointsArray.end(); ++aPos) { + aValue = anAttribute->pnt(aPos - aPointsArray.begin()); + if (aValue->distance(PlaneGCSSolver_Tools::point(*aPos)) > tolerance) + break; + } + int aShift = aPos - aPointsArray.begin(); + aPointsArray.insert(aPos, createPoint(aValue, myStorage)); + aPos = aPointsArray.begin() + aShift; } while (anAttribute->size() < (int)aPointsArray.size()) { @@ -241,7 +250,7 @@ bool PlaneGCSSolver_AttributeBuilder::updateAttribute( aParamsToRemove = PlaneGCSSolver_Tools::parameters(aWrapper); std::shared_ptr aNewArray = std::dynamic_pointer_cast( - createAttribute(theAttribute)); + createScalarArray(theAttribute, myStorage)); aWrapper->setArray(aNewArray->array()); isUpdated = true; } diff --git a/src/SketchSolver/SketchSolver_ConstraintCoincidence.cpp b/src/SketchSolver/SketchSolver_ConstraintCoincidence.cpp index 990297396..c00dcfce8 100644 --- a/src/SketchSolver/SketchSolver_ConstraintCoincidence.cpp +++ b/src/SketchSolver/SketchSolver_ConstraintCoincidence.cpp @@ -339,3 +339,18 @@ void SketchSolver_ConstraintCoincidence::notify(const FeaturePtr& theFeatur } } } + +void SketchSolver_ConstraintCoincidence::adjustConstraint() +{ + if (myBaseConstraint->getKind() == SketchPlugin_ConstraintCoincidenceInternal::ID()) { + AttributeIntegerPtr anIndexA = myBaseConstraint->integer( + SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_A()); + AttributeIntegerPtr anIndexB = myBaseConstraint->integer( + SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B()); + if ((anIndexA && anIndexA->isInitialized()) || + (anIndexB && anIndexB->isInitialized())) { + remove(); + process(); + } + } +} diff --git a/src/SketchSolver/SketchSolver_ConstraintCoincidence.h b/src/SketchSolver/SketchSolver_ConstraintCoincidence.h index f520b75d0..4ed2b821c 100644 --- a/src/SketchSolver/SketchSolver_ConstraintCoincidence.h +++ b/src/SketchSolver/SketchSolver_ConstraintCoincidence.h @@ -52,6 +52,10 @@ protected: virtual void getAttributes(EntityWrapperPtr& theValue, std::vector& theAttributes); + /// \brief This method is used in derived objects to check consistency of constraint. + /// E.g. the distance between line and point may be signed. + virtual void adjustConstraint(); + protected: bool myInSolver; ///< shows the constraint is added to the solver EntityWrapperPtr myFeatureExtremities[2]; ///< extremities of a feature, a point is coincident to -- 2.39.2