From: azv Date: Sat, 28 Dec 2019 09:41:35 +0000 (+0300) Subject: Issue #17347: B-Splines in Sketcher X-Git-Tag: V9_5_0a1~54^2~30 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=17033ec80c6476438be509f68159bb4a025531c1;hp=3d981141e4597bcd049c69ebd7f4dddd2b0d824e;p=modules%2Fshaper.git Issue #17347: B-Splines in Sketcher Initial implementation of non-periodic B-spline curves. --- diff --git a/lcov_reports.sh b/lcov_reports.sh index cd094cf15..e2b48e3ca 100755 --- a/lcov_reports.sh +++ b/lcov_reports.sh @@ -9,8 +9,7 @@ lcov --capture --directory ${BUILD_DIR} --no-external --base-directory ${SOURCES # make a working copy of report cp -f coverage.info.noext covfile # remove all reports of GUI and external parts (for all the next kinds of reports) -# RefAttrList is unused type of attribute for now -for MASK in '*wrap*' 'moc_*' 'XAO_*' 'SketcherPrs_*' 'GeomAlgoImpl_*' 'ModuleBase_*' '*Widget*' '*Splitter*' '*RefAttrList*'; do +for MASK in '*wrap*' 'moc_*' 'XAO_*' 'SketcherPrs_*' 'GeomAlgoImpl_*' 'ModuleBase_*' '*Widget*' '*Splitter*'; do lcov -r covfile ${MASK} --output-file covfile_res -q mv -f covfile_res covfile done diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_EdgeBuilder.cpp b/src/GeomAlgoAPI/GeomAlgoAPI_EdgeBuilder.cpp index 9bfc1a50a..4e42c0a92 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_EdgeBuilder.cpp +++ b/src/GeomAlgoAPI/GeomAlgoAPI_EdgeBuilder.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -266,3 +267,52 @@ std::shared_ptr GeomAlgoAPI_EdgeBuilder::ellipticArc( aRes->setImpl(new TopoDS_Shape(anEdge)); return aRes; } + +GeomEdgePtr GeomAlgoAPI_EdgeBuilder::bspline(const std::vector& thePoles, + const std::vector& theWeights, + const bool thePeriodic) +{ + int aDegree = 3; + if ((int)thePoles.size() <= aDegree) + aDegree = (int)thePoles.size() - 1; + if (aDegree <= 0) + return GeomEdgePtr(); + + int aNbKnots = (int)thePoles.size() - aDegree + 1; + + // collect arrays of poles, weights, knots and multiplicities + TColgp_Array1OfPnt aPoles(1, (int)thePoles.size()); + TColStd_Array1OfReal aWeights(1, (int)theWeights.size()); + TColStd_Array1OfReal aKnots(1, aNbKnots); + TColStd_Array1OfInteger aMults(1, aNbKnots); + + int anIndex = 1; + for (std::vector::const_iterator aPIt = thePoles.begin(); + aPIt != thePoles.end(); ++aPIt, ++anIndex) + aPoles.SetValue(anIndex, gp_Pnt((*aPIt)->x(), (*aPIt)->y(), (*aPIt)->z())); + anIndex = 1; + for (std::vector::const_iterator aWIt = theWeights.begin(); + aWIt != theWeights.end(); ++aWIt, ++anIndex) + aWeights.SetValue(anIndex, *aWIt); + anIndex = 1; + static const double aStartParam = 0.0; + static const double aEndParam = 1.0; + double aStep = aEndParam / (aNbKnots - 1); + for (double aKnot = aStartParam; anIndex < aNbKnots; ++anIndex, aKnot += aStep) + aKnots.SetValue(anIndex, aKnot); + aKnots.ChangeLast() = aEndParam; + anIndex = 1; + aMults.SetValue(anIndex, aDegree + 1); + for (++anIndex; anIndex < aNbKnots; ++anIndex) + aMults.SetValue(anIndex, 1); + aMults.SetValue(aNbKnots, aDegree + 1); + + Handle(Geom_BSplineCurve) aCurve = + new Geom_BSplineCurve(aPoles, aWeights, aKnots, aMults, aDegree, thePeriodic); + + BRepBuilderAPI_MakeEdge anEdgeBuilder(aCurve, aStartParam, aEndParam); + GeomEdgePtr aRes(new GeomAPI_Edge); + TopoDS_Edge anEdge = anEdgeBuilder.Edge(); + aRes->setImpl(new TopoDS_Shape(anEdge)); + return aRes; +} diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_EdgeBuilder.h b/src/GeomAlgoAPI/GeomAlgoAPI_EdgeBuilder.h index 245e97f05..d6185f3f5 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_EdgeBuilder.h +++ b/src/GeomAlgoAPI/GeomAlgoAPI_EdgeBuilder.h @@ -27,6 +27,7 @@ #include #include #include +#include /**\class GeomAlgoAPI_EdgeBuilder * \ingroup DataAlgo @@ -89,6 +90,11 @@ class GEOMALGOAPI_EXPORT GeomAlgoAPI_EdgeBuilder const double theMinorRadius, const std::shared_ptr& theStart, const std::shared_ptr& theEnd); + + /// Creates B-spline edge + static GeomEdgePtr bspline(const std::vector& thePoles, + const std::vector& theWeights, + const bool thePeriodic); }; #endif diff --git a/src/GeomData/CMakeLists.txt b/src/GeomData/CMakeLists.txt index d6ef69230..2b281396c 100644 --- a/src/GeomData/CMakeLists.txt +++ b/src/GeomData/CMakeLists.txt @@ -24,12 +24,14 @@ SET(PROJECT_HEADERS GeomData_Point.h GeomData_Dir.h GeomData_Point2D.h + GeomData_Point2DArray.h ) SET(PROJECT_SOURCES GeomData_Point.cpp GeomData_Dir.cpp GeomData_Point2D.cpp + GeomData_Point2DArray.cpp ) SET(PROJECT_LIBRARIES diff --git a/src/GeomData/GeomData_Point2DArray.cpp b/src/GeomData/GeomData_Point2DArray.cpp new file mode 100644 index 000000000..27376b988 --- /dev/null +++ b/src/GeomData/GeomData_Point2DArray.cpp @@ -0,0 +1,115 @@ +// Copyright (C) 2019-2020 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 "GeomData_Point2DArray.h" + +#include + +#include +#include +#include +#include + +#include + +GeomData_Point2DArray::GeomData_Point2DArray(TDF_Label& theLabel) + : myLab(theLabel) +{ + reinit(); +} + +void GeomData_Point2DArray::reinit() +{ + // check the attribute could be already presented in this doc (after load document) + myIsInitialized = myLab.FindAttribute(TDataStd_RealArray::GetID(), myArray) == Standard_True; +} + +bool GeomData_Point2DArray::assign(std::shared_ptr theOther) +{ + std::shared_ptr anOther = + std::dynamic_pointer_cast(theOther); + if (!anOther) + return false; + + setSize(anOther->size()); + myArray->ChangeArray(anOther->myArray->Array(), false); +} + +int GeomData_Point2DArray::size() +{ + if (myArray.IsNull() || !myArray->IsValid()) { + // this could be on undo and then redo creation of the attribute + // in result creation it may be uninitialized + myIsInitialized = myLab.FindAttribute(TDataStd_RealArray::GetID(), myArray) == Standard_True; + } + // checking the validity because attribute (as a field) may be presented, + // but without label: it is undoed + return (myArray.IsNull() || !myArray->IsValid()) ? 0 : myArray->Length() / 2; +} + +void GeomData_Point2DArray::setSize(const int theSize) +{ + int aValuesSize = 2 * theSize; + if (myArray.IsNull() || !myArray->IsValid()) { // create array if it is not done yet + if (aValuesSize != 0) { // if size is zero, nothing to do (null array means there is no array) + myArray = TDataStd_RealArray::Set(myLab, 0, aValuesSize - 1); + owner()->data()->sendAttributeUpdated(this); + } + } + else { // reset the old array + if (aValuesSize) { + if (aValuesSize != myArray->Length()) { // old data is not keept, a new array is created + Handle(TColStd_HArray1OfReal) aNewArray = new TColStd_HArray1OfReal(0, aValuesSize - 1); + myArray->ChangeArray(aNewArray); + owner()->data()->sendAttributeUpdated(this); + } + } + else { // size is zero => array must be erased + if (!myArray.IsNull()) { + myArray.Nullify(); + myLab.ForgetAttribute(TDataStd_RealArray::GetID()); + owner()->data()->sendAttributeUpdated(this); + } + } + } +} + +void GeomData_Point2DArray::setPnt(const int theIndex, + const double theX, + const double theY) +{ + if (myArray->Value(2 * theIndex) != theX || myArray->Value(2 * theIndex + 1) != theY) { + myArray->SetValue(2 * theIndex, theX); + myArray->SetValue(2 * theIndex + 1, theY); + owner()->data()->sendAttributeUpdated(this); + } +} + +void GeomData_Point2DArray::setPnt(const int theIndex, const GeomPnt2dPtr& thePoint) +{ + setPnt(theIndex, thePoint->x(), thePoint->y()); +} + +GeomPnt2dPtr GeomData_Point2DArray::pnt(const int theIndex) +{ + GeomPnt2dPtr aPoint; + if (theIndex >= 0 && theIndex * 2 < myArray->Length()) + aPoint.reset(new GeomAPI_Pnt2d(myArray->Value(2 * theIndex), myArray->Value(2 * theIndex + 1))); + return aPoint; +} diff --git a/src/GeomData/GeomData_Point2DArray.h b/src/GeomData/GeomData_Point2DArray.h new file mode 100644 index 000000000..8f3f7399e --- /dev/null +++ b/src/GeomData/GeomData_Point2DArray.h @@ -0,0 +1,70 @@ +// Copyright (C) 2019-2020 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 +// + +#ifndef GeomData_Point2DArray_H_ +#define GeomData_Point2DArray_H_ + +#include "GeomData.h" + +#include + +#include +#include + +/** \class GeomData_Point2DArray + * \ingroup DataModel + * \brief Attribute that contains an array of 2D points. + */ +class GeomData_Point2DArray : public GeomDataAPI_Point2DArray +{ + TDF_Label myLab; ///< the main label of the attribute + Handle_TDataStd_RealArray myArray; ///< array that keeps all coordinates of the points + +public: + /// Copy values from another array + /// \return \c true if the copy was successful + GEOMDATA_EXPORT virtual bool assign(std::shared_ptr theOther); + + /// Returns the size of the array (zero means that it is empty) + GEOMDATA_EXPORT virtual int size(); + + /// Sets the new size of the array. The previous data is erased. + GEOMDATA_EXPORT virtual void setSize(const int theSize); + + /// Defines the value of the array by index [0; size-1] + GEOMDATA_EXPORT virtual void setPnt(const int theIndex, + const double theX, const double theY); + + /// Defines the value of the array by index [0; size-1] + GEOMDATA_EXPORT virtual void setPnt(const int theIndex, + const std::shared_ptr& thePoint); + + /// Returns the value by the index + GEOMDATA_EXPORT virtual std::shared_ptr pnt(const int theIndex); + +protected: + /// Initializes attributes + GEOMDATA_EXPORT GeomData_Point2DArray(TDF_Label& theLabel); + /// Reinitializes the internal state of the attribute (may be needed on undo/redo, abort, etc) + virtual void reinit(); + + friend class Model_Data; +}; + +#endif diff --git a/src/GeomDataAPI/CMakeLists.txt b/src/GeomDataAPI/CMakeLists.txt index ecc087c26..fde2672d6 100644 --- a/src/GeomDataAPI/CMakeLists.txt +++ b/src/GeomDataAPI/CMakeLists.txt @@ -27,12 +27,14 @@ SET(PROJECT_HEADERS GeomDataAPI_Point.h GeomDataAPI_Dir.h GeomDataAPI_Point2D.h + GeomDataAPI_Point2DArray.h ) SET(PROJECT_SOURCES GeomDataAPI_Point.cpp GeomDataAPI_Dir.cpp GeomDataAPI_Point2D.cpp + GeomDataAPI_Point2DArray.cpp ) SET(PROJECT_LIBRARIES diff --git a/src/GeomDataAPI/GeomDataAPI_Point2DArray.cpp b/src/GeomDataAPI/GeomDataAPI_Point2DArray.cpp new file mode 100644 index 000000000..fcd9a8e6d --- /dev/null +++ b/src/GeomDataAPI/GeomDataAPI_Point2DArray.cpp @@ -0,0 +1,33 @@ +// Copyright (C) 2019-2020 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 + +std::string GeomDataAPI_Point2DArray::attributeType() +{ + return typeId(); +} + +GeomDataAPI_Point2DArray::GeomDataAPI_Point2DArray() +{ +} + +GeomDataAPI_Point2DArray::~GeomDataAPI_Point2DArray() +{ +} diff --git a/src/GeomDataAPI/GeomDataAPI_Point2DArray.h b/src/GeomDataAPI/GeomDataAPI_Point2DArray.h new file mode 100644 index 000000000..1976b16ee --- /dev/null +++ b/src/GeomDataAPI/GeomDataAPI_Point2DArray.h @@ -0,0 +1,74 @@ +// Copyright (C) 2019-2020 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 +// + +#ifndef GeomDataAPI_Point2DArray_H_ +#define GeomDataAPI_Point2DArray_H_ + +#include +#include + +class GeomAPI_Pnt2d; + +/**\class GeomDataAPI_Point2DArray + * \ingroup DataModel + * \brief Attribute that contains array of 2D point coordinates. + */ + +class GeomDataAPI_Point2DArray : public ModelAPI_Attribute +{ +public: + /// Copy values from another array + /// \return \c true if the copy was successful + GEOMDATAAPI_EXPORT virtual bool assign(std::shared_ptr theOther) = 0; + + /// Returns the size of the array (zero means that it is empty) + GEOMDATAAPI_EXPORT virtual int size() = 0; + + /// Sets the new size of the array. The previous data is erased. + GEOMDATAAPI_EXPORT virtual void setSize(const int theSize) = 0; + + /// Defines the value of the array by index [0; size-1] + GEOMDATAAPI_EXPORT virtual void setPnt(const int theIndex, + const double theX, const double theY) = 0; + + /// Defines the value of the array by index [0; size-1] + GEOMDATAAPI_EXPORT virtual void setPnt(const int theIndex, + const std::shared_ptr& thePoint) = 0; + + /// Returns the value by the index + GEOMDATAAPI_EXPORT virtual std::shared_ptr pnt(const int theIndex) = 0; + + /// Returns the type of this class of attributes + static std::string typeId() + { + return std::string("Point2DArray"); + } + + /// Returns the type of this class of attributes, not static method + GEOMDATAAPI_EXPORT virtual std::string attributeType(); + +protected: + /// Objects are created for features automatically + GEOMDATAAPI_EXPORT GeomDataAPI_Point2DArray(); + GEOMDATAAPI_EXPORT virtual ~GeomDataAPI_Point2DArray(); +}; + +typedef std::shared_ptr AttributePoint2DArrayPtr; + +#endif diff --git a/src/Model/Model_AttributeRefAttrList.cpp b/src/Model/Model_AttributeRefAttrList.cpp index db780eac7..5ce42aef3 100644 --- a/src/Model/Model_AttributeRefAttrList.cpp +++ b/src/Model/Model_AttributeRefAttrList.cpp @@ -27,8 +27,13 @@ void Model_AttributeRefAttrList::append(ObjectPtr theObject) { - std::shared_ptr aData = std::dynamic_pointer_cast(theObject->data()); - myRef->Append(aData->label().Father()); // store label of the object + TDF_Label aLabel; + if (theObject) { + std::shared_ptr aData = std::dynamic_pointer_cast(theObject->data()); + aLabel = aData->label().Father(); + } + + myRef->Append(aLabel); // store label of the object myIDs->Append(""); // for the object store an empty string // do it before the transaction finish to make just created/removed objects know dependencies // and reference from composite feature is removed automatically @@ -306,7 +311,9 @@ void Model_AttributeRefAttrList::remove(const std::set& theIndices) myIDs->Append(anIDIter.Value()); } else { // found, so need to update the dependencies aOneisDeleted = true; - ObjectPtr anObj = aDoc->objects()->object(aRefIter.Value()); + ObjectPtr anObj; + if (!aRefIter.Value().IsNull()) + anObj = aDoc->objects()->object(aRefIter.Value()); if (anObj.get()) { REMOVE_BACK_REF(anObj); } diff --git a/src/Model/Model_Data.cpp b/src/Model/Model_Data.cpp index e5641930e..2a25c5136 100644 --- a/src/Model/Model_Data.cpp +++ b/src/Model/Model_Data.cpp @@ -49,9 +49,11 @@ #include #include +#include #include #include +#include #include #include #include @@ -217,7 +219,10 @@ AttributePtr Model_Data::addAttribute( } anAttribute->myIsInitialized = anAllInitialized; anAttr = anAttribute; + } else if (theAttrType == GeomData_Point2DArray::typeId()) { + anAttr = new GeomData_Point2DArray(anAttrLab); } + if (anAttr) { aResult = std::shared_ptr(anAttr); myAttrs[theID] = std::pair(aResult, anAttrIndex); diff --git a/src/ModelAPI/ModelAPI_Events.cpp b/src/ModelAPI/ModelAPI_Events.cpp index f1bcd1e26..92474383c 100644 --- a/src/ModelAPI/ModelAPI_Events.cpp +++ b/src/ModelAPI/ModelAPI_Events.cpp @@ -335,10 +335,12 @@ void ModelAPI_ObjectMovedMessage::setMovedObject(const ObjectPtr& theMovedObject myMovedAttribute = AttributePtr(); } -void ModelAPI_ObjectMovedMessage::setMovedAttribute(const AttributePtr& theMovedAttribute) +void ModelAPI_ObjectMovedMessage::setMovedAttribute(const AttributePtr& theMovedAttribute, + const int thePointIndex) { myMovedAttribute = theMovedAttribute; myMovedObject = ObjectPtr(); + myMovedPointIndex = thePointIndex; } void ModelAPI_ObjectMovedMessage::setOriginalPosition(double theX, double theY) diff --git a/src/ModelAPI/ModelAPI_Events.h b/src/ModelAPI/ModelAPI_Events.h index 0b250ebd5..02fdadb09 100644 --- a/src/ModelAPI/ModelAPI_Events.h +++ b/src/ModelAPI/ModelAPI_Events.h @@ -486,6 +486,7 @@ class ModelAPI_ObjectMovedMessage : public Events_Message { ObjectPtr myMovedObject; AttributePtr myMovedAttribute; + int myMovedPointIndex; std::shared_ptr myOriginalPosition; std::shared_ptr myCurrentPosition; @@ -496,7 +497,10 @@ public: /// Set object which is being moved (if the message already contains attribute it will be cleared) MODELAPI_EXPORT void setMovedObject(const ObjectPtr& theMovedObject); /// Set attribute which is being moved (if the message already contains object it will be cleared) - MODELAPI_EXPORT void setMovedAttribute(const AttributePtr& theMovedAttribute); + /// \param[in] theMovedAttribute moved attribute + /// \param[in] thePointIndex index of the point if the moved attribute is an array of points + MODELAPI_EXPORT void setMovedAttribute(const AttributePtr& theMovedAttribute, + const int thePointIndex = -1); /// Return moved object ObjectPtr movedObject() const @@ -504,6 +508,9 @@ public: /// Return moved attribute AttributePtr movedAttribute() const { return myMovedAttribute; } + /// Return index of the moved point + int movedPointIndex() const + { return myMovedPointIndex; } /// Set original mouse position MODELAPI_EXPORT void setOriginalPosition(double theX, double theY); diff --git a/src/PartSet/CMakeLists.txt b/src/PartSet/CMakeLists.txt index 83789b24f..0384951d6 100644 --- a/src/PartSet/CMakeLists.txt +++ b/src/PartSet/CMakeLists.txt @@ -59,8 +59,9 @@ SET(PROJECT_HEADERS PartSet_WidgetSketchLabel.h PartSet_CenterPrs.h PartSet_ExternalPointsMgr.h - PartSet_TreeNodes.h - PartSet_FieldStepPrs.h + PartSet_TreeNodes.h + PartSet_FieldStepPrs.h + PartSet_WidgetBSplinePoints.h ) SET(PROJECT_MOC_HEADERS @@ -78,6 +79,7 @@ SET(PROJECT_MOC_HEADERS PartSet_WidgetShapeSelector.h PartSet_WidgetSketchCreator.h PartSet_WidgetSketchLabel.h + PartSet_WidgetBSplinePoints.h PartSet_ExternalPointsMgr.h ) @@ -109,8 +111,9 @@ SET(PROJECT_SOURCES PartSet_WidgetSketchLabel.cpp PartSet_CenterPrs.cpp PartSet_ExternalPointsMgr.cpp - PartSet_TreeNodes.cpp - PartSet_FieldStepPrs.cpp + PartSet_TreeNodes.cpp + PartSet_FieldStepPrs.cpp + PartSet_WidgetBSplinePoints.cpp ) SET(PROJECT_RESOURCES diff --git a/src/PartSet/PartSet_Module.cpp b/src/PartSet/PartSet_Module.cpp index 5b5068971..9c70d7e9f 100644 --- a/src/PartSet/PartSet_Module.cpp +++ b/src/PartSet/PartSet_Module.cpp @@ -22,6 +22,7 @@ #include "PartSet_Validators.h" #include "PartSet_Tools.h" #include "PartSet_PreviewPlanes.h" +#include "PartSet_WidgetBSplinePoints.h" #include "PartSet_WidgetPoint2d.h" #include "PartSet_WidgetPoint2DFlyout.h" #include "PartSet_WidgetShapeSelector.h" @@ -915,6 +916,12 @@ ModuleBase_ModelWidget* PartSet_Module::createWidgetByType(const std::string& th aPointSelectorWgt->setSketcher(mySketchMgr->activeSketch()); aWgt = aPointSelectorWgt; } + else if (theType == "sketch-bspline_selector") { + PartSet_WidgetBSplinePoints* aBSplineWgt = + new PartSet_WidgetBSplinePoints(theParent, aWorkshop, theWidgetApi); + aBSplineWgt->setSketch(mySketchMgr->activeSketch()); + aWgt = aBSplineWgt; + } else if (theType == WDG_DOUBLEVALUE_EDITOR) { aWgt = new PartSet_WidgetEditor(theParent, aWorkshop, theWidgetApi); } else if (theType == "export_file_selector") { diff --git a/src/PartSet/PartSet_Tools.cpp b/src/PartSet/PartSet_Tools.cpp index fbb50228f..759f45bea 100644 --- a/src/PartSet/PartSet_Tools.cpp +++ b/src/PartSet/PartSet_Tools.cpp @@ -483,6 +483,24 @@ std::shared_ptr PartSet_Tools::getPnt2d(QMouseEvent* theEvent, return std::shared_ptr(new GeomAPI_Pnt2d(aX, anY)); } +std::shared_ptr PartSet_Tools::getPnt2d(const Handle(V3d_View)& theView, + const TopoDS_Shape& theShape, + const FeaturePtr& theSketch) +{ + GeomPnt2dPtr aPoint2D; + if (!theShape.IsNull() && theShape.ShapeType() == TopAbs_VERTEX) { + const TopoDS_Vertex& aVertex = TopoDS::Vertex(theShape); + if (!aVertex.IsNull()) { + // the case when the point is taken from the existing vertex + gp_Pnt aPoint = BRep_Tool::Pnt(aVertex); + double aX, aY; + PartSet_Tools::convertTo2D(aPoint, theSketch, theView, aX, aY); + aPoint2D.reset(new GeomAPI_Pnt2d(aX, aY)); + } + } + return aPoint2D; +} + FeaturePtr findFirstCoincidenceByData(const DataPtr& theData, std::shared_ptr thePoint) { diff --git a/src/PartSet/PartSet_Tools.h b/src/PartSet/PartSet_Tools.h index 7f1c236ab..742de4f59 100644 --- a/src/PartSet/PartSet_Tools.h +++ b/src/PartSet/PartSet_Tools.h @@ -215,7 +215,7 @@ public: /** * Convertes parameters into a geom point - * \theEvent a Qt event to find mouse position + * \param theEvent a Qt event to find mouse position * \param theWindow view window to define eye of view * \param theSketch to convert 3D point coordinates into coorditates of the sketch plane */ @@ -223,6 +223,16 @@ public: ModuleBase_IViewWindow* theWindow, const FeaturePtr& theSketch); + /** Returns point 2d from selected shape + * \param theView a view window + * \param theShape a vertex shape + * \param theX an output value of X coordinate + * \param theY an output value of Y coordinate + */ + static std::shared_ptr getPnt2d(const Handle(V3d_View)& theView, + const TopoDS_Shape& theShape, + const FeaturePtr& theSketch); + /** * Gets all references to the feature, take coincidence constraint features, get point 2d attributes * and compare the point value to be equal with the given. Returns the first feature, which has diff --git a/src/PartSet/PartSet_WidgetBSplinePoints.cpp b/src/PartSet/PartSet_WidgetBSplinePoints.cpp new file mode 100644 index 000000000..5050b3355 --- /dev/null +++ b/src/PartSet/PartSet_WidgetBSplinePoints.cpp @@ -0,0 +1,641 @@ +// Copyright (C) 2019-2020 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 + +#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 + +static const double MaxCoordinate = 1e12; + +PartSet_WidgetBSplinePoints::PartSet_WidgetBSplinePoints(QWidget* theParent, + ModuleBase_IWorkshop* theWorkshop, + const Config_WidgetAPI* theData) +: ModuleBase_ModelWidget(theParent, theData), myWorkshop(theWorkshop), + myValueIsCashed(false), myIsFeatureVisibleInCash(true), + myXValueInCash(0), myYValueInCash(0), + myPointIndex(0), myFinished(false) +{ + myRefAttribute = theData->getProperty("reference_attribute"); + + // the control should accept the focus, so the boolean flag is corrected to be true + myIsObligatory = true; + QString aPageName = translate(theData->getProperty(CONTAINER_PAGE_NAME)); + myGroupBox = new QGroupBox(aPageName, theParent); + myGroupBox->setFlat(false); + + bool aAcceptVariables = theData->getBooleanAttribute(DOUBLE_WDG_ACCEPT_EXPRESSIONS, true); + + // B-spline weights attribute + myWeightsAttr = theData->getProperty("weights"); + + QGridLayout* aGroupLay = new QGridLayout(myGroupBox); + ModuleBase_Tools::adjustMargins(aGroupLay); + aGroupLay->setSpacing(4); + aGroupLay->setColumnStretch(1, 1); + createNextPoint(); + + QVBoxLayout* aLayout = new QVBoxLayout(this); + ModuleBase_Tools::zeroMargins(aLayout); + aLayout->addWidget(myGroupBox); + setLayout(aLayout); + + myWidgetValidator = new ModuleBase_WidgetValidator(this, myWorkshop); + myExternalObjectMgr = new PartSet_ExternalObjectsMgr(theData->getProperty("use_external"), + theData->getProperty("can_create_external"), true); +} + +void PartSet_WidgetBSplinePoints::createNextPoint() +{ + storeCurentValue(); + + QGridLayout* aGroupLay = dynamic_cast(myGroupBox->layout()); + int row = (int)(myXSpin.size() + myWeightSpin.size()); + + QString aPoleStr = tr("Pole %1"); + aPoleStr = aPoleStr.arg(myXSpin.size() + 1); + + QGroupBox* aPoleGroupBox = new QGroupBox(aPoleStr, myGroupBox); + QGridLayout* aPoleLay = new QGridLayout(aPoleGroupBox); + ModuleBase_Tools::adjustMargins(aPoleLay); + aPoleLay->setSpacing(2); + aPoleLay->setColumnStretch(1, 1); + + myXSpin.push_back(new ModuleBase_LabelValue(aPoleGroupBox, tr("X"))); + aPoleLay->addWidget(myXSpin.back(), 0, 1); + myYSpin.push_back(new ModuleBase_LabelValue(aPoleGroupBox, tr("Y"))); + aPoleLay->addWidget(myYSpin.back(), 1, 1); + + aGroupLay->addWidget(aPoleGroupBox, row, 1); + + QString aWeightStr = tr("Weight %1"); + aWeightStr = aWeightStr.arg(myWeightSpin.size() + 1); + + myWeightSpin.push_back(new ModuleBase_LabelValue(myGroupBox, aWeightStr)); + aGroupLay->addWidget(myWeightSpin.back(), ++row, 1); +} + +void PartSet_WidgetBSplinePoints::removeLastPoint() +{ + QGridLayout* aGroupLay = dynamic_cast(myGroupBox->layout()); + aGroupLay->removeWidget(myWeightSpin.back()); + myWeightSpin.pop_back(); + aGroupLay->removeWidget(myYSpin.back()); + aGroupLay->removeWidget(myXSpin.back()); + aGroupLay->removeWidget(myXSpin.back()->parentWidget()); + myYSpin.pop_back(); + myXSpin.pop_back(); + + // update B-spline feature attributes + storeValueCustom(); +} + +bool PartSet_WidgetBSplinePoints::isValidSelectionCustom(const ModuleBase_ViewerPrsPtr& theValue) +{ + bool aValid = true; + + PartSet_Module* aModule = dynamic_cast(myWorkshop->module()); + if (aModule->sketchReentranceMgr()->isInternalEditActive()) + return true; // when internal edit is started a new feature is created. I has not results, AIS + + // the selection is not possible if the current feature has no presentation for the current + // attribute not in AIS not in results. If so, no object in current feature where make + // coincidence, so selection is not necessary + GeomShapePtr anAISShape; + GeomPresentablePtr aPrs = std::dynamic_pointer_cast(myFeature); + if (aPrs.get()) { + AISObjectPtr anAIS; + anAIS = aPrs->getAISObject(anAIS); + if (anAIS.get()) { + anAISShape = anAIS->getShape(); + } + } + const std::list >& aResults = myFeature->results(); + if (!anAISShape.get() && aResults.empty()) + return true; + + AttributeRefAttrListPtr aRefAttrList = attributeRefAttrList(); + if (aRefAttrList) + return isValidSelectionForAttribute_(theValue, myFeature->attribute(attributeID())); + return true; +} + +bool PartSet_WidgetBSplinePoints::isValidSelectionForAttribute_( + const ModuleBase_ViewerPrsPtr& theValue, + const AttributePtr& theAttribute) +{ + bool aValid = false; + + // stores the current values of the widget attribute + bool isFlushesActived, isAttributeSetInitializedBlocked, isAttributeSendUpdatedBlocked; + + AttributeRefAttrListPtr aRefAttrList = attributeRefAttrList(); + ModuleBase_WidgetValidated::blockFeatureAttribute(aRefAttrList, myFeature, true, + isFlushesActived, isAttributeSetInitializedBlocked, isAttributeSendUpdatedBlocked); + myWidgetValidator->storeAttributeValue(aRefAttrList); + + // saves the owner value to the widget attribute + aValid = setSelectionCustom(theValue); + if (aValid) + // checks the attribute validity + aValid = myWidgetValidator->isValidAttribute(theAttribute); + + // restores the current values of the widget attribute + myWidgetValidator->restoreAttributeValue(aRefAttrList, aValid); + myExternalObjectMgr->removeExternal(sketch(), myFeature, myWorkshop, true); + + ModuleBase_WidgetValidated::blockFeatureAttribute(aRefAttrList, myFeature, false, + isFlushesActived, isAttributeSetInitializedBlocked, isAttributeSendUpdatedBlocked); + return aValid; +} + +bool PartSet_WidgetBSplinePoints::setSelectionCustom(const ModuleBase_ViewerPrsPtr& theValue) +{ + bool isDone = false; + GeomShapePtr aShape = theValue->shape(); + if (aShape.get() && !aShape->isNull()) { + Handle(V3d_View) aView = myWorkshop->viewer()->activeView(); + const TopoDS_Shape& aTDShape = aShape->impl(); + GeomPnt2dPtr aPnt = PartSet_Tools::getPnt2d(aView, aTDShape, mySketch); + if (aPnt) { + fillRefAttribute(aPnt, theValue); + isDone = true; + } + else if (aTDShape.ShapeType() == TopAbs_EDGE) { + fillRefAttribute(theValue); + isDone = true; + } + } + return isDone; +} + +static void fillLabels(std::vector& theLabels, const double theValue) +{ + for (std::vector::iterator anIt = theLabels.begin(); + anIt != theLabels.end(); ++anIt) + (*anIt)->setValue(theValue); +} + +bool PartSet_WidgetBSplinePoints::resetCustom() +{ + bool aDone = false; + if (!isUseReset() || isComputedDefault()) + aDone = false; + else { + if (myValueIsCashed) { + // if the restored value should be hidden, aDone = true to set + // reset state for the widget in the parent + aDone = restoreCurentValue(); + emit objectUpdated(); + } + else { + // it is important to block the spin box control in order to do not through out the + // locking of the validating state. + fillLabels(myXSpin, 0.0); + fillLabels(myYSpin, 0.0); + fillLabels(myWeightSpin, 1.0); + + storeValueCustom(); + aDone = true; + } + } + return aDone; +} + +PartSet_WidgetBSplinePoints::~PartSet_WidgetBSplinePoints() +{ + delete myExternalObjectMgr; +} + +bool PartSet_WidgetBSplinePoints::setPoint(double theX, double theY) +{ + if (fabs(theX) >= MaxCoordinate || fabs(theY) >= MaxCoordinate) + return false; + + myXSpin.back()->setValue(theX); + myYSpin.back()->setValue(theY); + myWeightSpin.back()->setValue(1.0); + + storeValue(); + return true; +} + +void PartSet_WidgetBSplinePoints::storePolesAndWeights() const +{ + std::shared_ptr aData = myFeature->data(); + AttributePoint2DArrayPtr aPointArray = std::dynamic_pointer_cast( + aData->attribute(attributeID())); + AttributeDoubleArrayPtr aWeightsArray = aData->realArray(myWeightsAttr); + + aPointArray->setSize((int)myXSpin.size()); + aWeightsArray->setSize((int)myWeightSpin.size()); + + std::vector::const_iterator aXIt = myXSpin.begin(); + std::vector::const_iterator aYIt = myYSpin.begin(); + for (int anIndex = 0; aXIt != myXSpin.end() && aYIt != myYSpin.end(); ++anIndex, ++aXIt, ++aYIt) + aPointArray->setPnt(anIndex, (*aXIt)->value(), (*aYIt)->value()); + + std::vector::const_iterator aWIt = myWeightSpin.begin(); + for (int anIndex = 0; aWIt != myWeightSpin.end(); ++anIndex, ++aWIt) + aWeightsArray->setValue(anIndex, (*aWIt)->value()); +} + +bool PartSet_WidgetBSplinePoints::storeValueCustom() +{ + std::shared_ptr aData = myFeature->data(); + if (!aData || !aData->isValid()) // can be on abort of sketcher element + return false; + AttributePoint2DArrayPtr aPointArray = std::dynamic_pointer_cast( + aData->attribute(attributeID())); + AttributeDoubleArrayPtr aWeightsArray = aData->realArray(myWeightsAttr); + + PartSet_WidgetBSplinePoints* that = (PartSet_WidgetBSplinePoints*) this; + bool isBlocked = that->blockSignals(true); + bool isImmutable = aPointArray->setImmutable(true); + + if (myFeature->isMacro()) { + // Moving points of macro-features has been processed directly (without solver) + storePolesAndWeights(); + updateObject(myFeature); + + } else { + if (!aPointArray->isInitialized()) { + storePolesAndWeights(); + } + + std::shared_ptr aMessage( + new ModelAPI_ObjectMovedMessage(this)); + aMessage->setMovedAttribute(aPointArray, aPointArray->size() - 1); + aMessage->setOriginalPosition(aPointArray->pnt(aPointArray->size() - 1)); + aMessage->setCurrentPosition(myXSpin.back()->value(), myYSpin.back()->value()); + Events_Loop::loop()->send(aMessage); + } + + aPointArray->setImmutable(isImmutable); + that->blockSignals(isBlocked); + + return true; +} + +bool PartSet_WidgetBSplinePoints::restoreValueCustom() +{ + std::shared_ptr aData = myFeature->data(); + AttributePoint2DArrayPtr aPointArray = std::dynamic_pointer_cast( + aData->attribute(attributeID())); + AttributeDoubleArrayPtr aWeightsArray = aData->realArray(myWeightsAttr); + + if (aPointArray->isInitialized()) { + while (myXSpin.size() < aPointArray->size()) + createNextPoint(); + + std::vector::iterator aXIt = myXSpin.begin(); + std::vector::iterator aYIt = myYSpin.begin(); + for (int anIndex = 0; aXIt != myXSpin.end() && aYIt != myYSpin.end(); + ++anIndex, ++aXIt, ++aYIt) { + GeomPnt2dPtr aPoint = aPointArray->pnt(anIndex); + (*aXIt)->setValue(aPoint->x()); + (*aYIt)->setValue(aPoint->y()); + } + + std::vector::iterator aWIt = myWeightSpin.begin(); + for (int anIndex = 0; aWIt != myWeightSpin.end(); ++anIndex, ++aWIt) + (*aWIt)->setValue(aWeightsArray->value(anIndex)); + } + else { + if (myXSpin.empty()) + createNextPoint(); + + myXSpin.back()->setValue(0.0); + myYSpin.back()->setValue(0.0); + myWeightSpin.back()->setValue(0.0); + } + + return true; +} + +static void storeArray(const std::vector& theLabels, + std::vector& theValues) +{ + theValues.clear(); + theValues.reserve(theLabels.size()); + for (std::vector::const_iterator anIt = theLabels.begin(); + anIt != theLabels.end(); ++anIt) + theValues.push_back((*anIt)->value()); +} + +void PartSet_WidgetBSplinePoints::storeCurentValue() +{ + myValueIsCashed = true; + myIsFeatureVisibleInCash = XGUI_Displayer::isVisible( + XGUI_Tools::workshop(myWorkshop)->displayer(), myFeature); + + storeArray(myXSpin, myXValueInCash); + storeArray(myYSpin, myYValueInCash); + storeArray(myWeightSpin, myWeightInCash); +} + +static void restoreArray(std::vector& theCacheValues, + std::vector& theLabels) +{ + std::vector::iterator aCIt = theCacheValues.begin(); + std::vector::iterator anIt = theLabels.begin(); + for (; anIt != theLabels.end(); ++anIt) { + if (aCIt != theCacheValues.end()) + (*anIt)->setValue(*aCIt++); + else + (*anIt)->setValue(0.0); + } + theCacheValues.clear(); +} + +bool PartSet_WidgetBSplinePoints::restoreCurentValue() +{ + bool aRestoredAndHidden = true; + + bool isVisible = myIsFeatureVisibleInCash; + + myValueIsCashed = false; + myIsFeatureVisibleInCash = true; + // fill the control widgets by the cashed value + restoreArray(myXValueInCash, myXSpin); + restoreArray(myYValueInCash, myYSpin); + restoreArray(myWeightInCash, myWeightSpin); + + // store value to the model + storeValueCustom(); + if (isVisible) { + setValueState(Stored); + aRestoredAndHidden = false; + } + else + aRestoredAndHidden = true; + + return aRestoredAndHidden; +} + +QList PartSet_WidgetBSplinePoints::getControls() const +{ + QList aControls; + std::vector::const_iterator aXIt = myXSpin.begin(); + std::vector::const_iterator aYIt = myYSpin.begin(); + std::vector::const_iterator aWIt = myWeightSpin.begin(); + for (; aXIt != myXSpin.end() && aYIt != myYSpin.end() && aWIt != myWeightSpin.end(); + ++aXIt, ++aYIt, ++aWIt) { + aControls.append(*aXIt); + aControls.append(*aYIt); + aControls.append(*aWIt); + } + return aControls; +} + +void PartSet_WidgetBSplinePoints::selectionModes(int& theModuleSelectionModes, QIntList& theModes) +{ + theModuleSelectionModes = -1; + theModes << TopAbs_VERTEX; + theModes << TopAbs_EDGE; +} + +void PartSet_WidgetBSplinePoints::deactivate() +{ + // the value of the control should be stored to model if it was not + // initialized yet. It is important when we leave this control by Tab key. + // It should not be performed by the widget activation as the preview + // is visualized with default value. Line point is moved to origin. + AttributePtr anAttribute = myFeature->data()->attribute(attributeID()); + if (anAttribute && !anAttribute->isInitialized()) + storeValue(); + + ModuleBase_ModelWidget::deactivate(); +} + +void PartSet_WidgetBSplinePoints::mouseReleased(ModuleBase_IViewWindow* theWindow, QMouseEvent* theEvent) +{ + // the contex menu release by the right button should not be processed by this widget + if (theEvent->button() != Qt::LeftButton) + return; + + ModuleBase_ISelection* aSelection = myWorkshop->selection(); + Handle(V3d_View) aView = theWindow->v3dView(); + + QList aList = aSelection->getSelected(ModuleBase_ISelection::Viewer); + ModuleBase_ViewerPrsPtr aFirstValue = + aList.size() > 0 ? aList.first() : ModuleBase_ViewerPrsPtr(); + if (!aFirstValue.get() && myPreSelected.get()) { + aFirstValue = myPreSelected; + } + + TopoDS_Shape aSelectedShape; + ObjectPtr aSelectedObject; + + // if we have selection and use it + if (aFirstValue.get() && isValidSelectionCustom(aFirstValue) && + aFirstValue->shape().get()) { // Trihedron Axis may be selected, but shape is empty + GeomShapePtr aGeomShape = aFirstValue->shape(); + aSelectedShape = aGeomShape->impl(); + aSelectedObject = aFirstValue->object(); + + FeaturePtr aSelectedFeature = ModelAPI_Feature::feature(aSelectedObject); + std::shared_ptr aSPFeature; + if (aSelectedFeature.get()) + aSPFeature = std::dynamic_pointer_cast(aSelectedFeature); + + bool isSketchExternalFeature = aSPFeature.get() && aSPFeature->isExternal(); + if ((!aSPFeature && !aSelectedShape.IsNull()) || isSketchExternalFeature) { + ObjectPtr aFixedObject = + PartSet_Tools::findFixedObjectByExternal(aSelectedShape, aSelectedObject, mySketch); + if (aFixedObject) + aSelectedObject = aFixedObject; + else if (!isSketchExternalFeature) { + FeaturePtr aCreatedFeature; + aSelectedObject = PartSet_Tools::createFixedObjectByExternal( + aGeomShape, aSelectedObject, mySketch, false, aCreatedFeature); + } + } + } + // The selection could be a center of an external circular object + else if (aFirstValue.get() && (!aFirstValue->interactive().IsNull())) { + Handle(PartSet_CenterPrs) aAIS = + Handle(PartSet_CenterPrs)::DownCast(aFirstValue->interactive()); + if (!aAIS.IsNull()) { + gp_Pnt aPntComp = aAIS->Component()->Pnt(); + GeomVertexPtr aVertPtr(new GeomAPI_Vertex(aPntComp.X(), aPntComp.Y(), aPntComp.Z())); + aSelectedShape = aVertPtr->impl(); + + aSelectedObject = + PartSet_Tools::findFixedObjectByExternal(aSelectedShape, aAIS->object(), mySketch); + if (!aSelectedObject.get()) + { + FeaturePtr aCreatedFeature; + aSelectedObject = PartSet_Tools::createFixedByExternalCenter(aAIS->object(), aAIS->edge(), + aAIS->centerType(), mySketch, false, aCreatedFeature); + } + } + } + + GeomPnt2dPtr aSelectedPoint = PartSet_Tools::getPnt2d(aView, aSelectedShape, mySketch); + if (aSelectedPoint) { + // nullify selected object to add reference to attribute instead of its owner + aSelectedObject = ObjectPtr(); + } + else { + aSelectedPoint = PartSet_Tools::getPnt2d(theEvent, theWindow, mySketch); + setValueState(Stored); // in case of edge selection, Apply state should also be updated + } + if (aSelectedObject) + fillRefAttribute(aSelectedObject); + else + fillRefAttribute(aSelectedPoint, aFirstValue); + + // next pole of B-spline + createNextPoint(); +} + +void PartSet_WidgetBSplinePoints::mouseMoved(ModuleBase_IViewWindow* theWindow, QMouseEvent* theEvent) +{ + PartSet_Module* aModule = dynamic_cast(myWorkshop->module()); + + if (myFinished || isEditingMode() || aModule->sketchReentranceMgr()->isInternalEditActive()) + return; + + gp_Pnt aPoint = PartSet_Tools::convertClickToPoint(theEvent->pos(), theWindow->v3dView()); + + double aX = 0, aY = 0; + PartSet_Tools::convertTo2D(aPoint, mySketch, theWindow->v3dView(), aX, aY); + if (myState != ModifiedInViewer) + storeCurentValue(); + // we need to block the value state change + bool isBlocked = blockValueState(true); + setPoint(aX, aY); + blockValueState(isBlocked); + setValueState(ModifiedInViewer); +} + +bool PartSet_WidgetBSplinePoints::processEscape() +{ + bool isProcessed = !isEditingMode(); + if (isProcessed) { + // remove widgets corrsponding to the last pole/weight of B-spline + removeLastPoint(); + myFinished = true; + + emit focusOutWidget(this); + } + return isProcessed; +} + +bool PartSet_WidgetBSplinePoints::useSelectedShapes() const +{ + return true; +} + +AttributeRefAttrListPtr PartSet_WidgetBSplinePoints::attributeRefAttrList() const +{ + if (myRefAttribute.empty()) + return AttributeRefAttrListPtr(); + + AttributePtr anAttributeRef = feature()->attribute(myRefAttribute); + if (!anAttributeRef.get()) + return AttributeRefAttrListPtr(); + + return std::dynamic_pointer_cast(anAttributeRef); +} + +void PartSet_WidgetBSplinePoints::fillRefAttribute(GeomPnt2dPtr theClickedPoint, + const std::shared_ptr& theValue) +{ + AttributeRefAttrListPtr aRefAttrList = attributeRefAttrList(); + if (!aRefAttrList.get()) + return; + + FeaturePtr aFeature = feature(); + std::string anAttribute = attributeID(); + + if (aFeature.get()) { + AttributePoint2DPtr aClickedFeaturePoint = + PartSet_WidgetPoint2D::findFirstEqualPointInSketch(mySketch, aFeature, theClickedPoint); + if (aClickedFeaturePoint.get()) + aRefAttrList->append(aClickedFeaturePoint); + else + fillRefAttribute(theValue); + } +} + +void PartSet_WidgetBSplinePoints::fillRefAttribute(const ModuleBase_ViewerPrsPtr& theValue) +{ + ObjectPtr anObject; + if (theValue) + anObject = getGeomSelection(theValue); + fillRefAttribute(anObject); +} + +void PartSet_WidgetBSplinePoints::fillRefAttribute(const ObjectPtr& theObject) +{ + AttributeRefAttrListPtr aRefAttrList = attributeRefAttrList(); + if (aRefAttrList.get()) + aRefAttrList->append(theObject); +} + +ObjectPtr PartSet_WidgetBSplinePoints::getGeomSelection(const ModuleBase_ViewerPrsPtr& theValue) +{ + ObjectPtr anObject; + GeomShapePtr aShape; + ModuleBase_ISelection* aSelection = myWorkshop->selection(); + anObject = aSelection->getResult(theValue); + aShape = aSelection->getShape(theValue); + myExternalObjectMgr->getGeomSelection(theValue, anObject, aShape, myWorkshop, sketch(), true); + + return anObject; +} diff --git a/src/PartSet/PartSet_WidgetBSplinePoints.h b/src/PartSet/PartSet_WidgetBSplinePoints.h new file mode 100644 index 000000000..f537dc866 --- /dev/null +++ b/src/PartSet/PartSet_WidgetBSplinePoints.h @@ -0,0 +1,188 @@ +// Copyright (C) 2019-2020 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 +// + +#ifndef PartSet_WidgetBSplinePoints_H +#define PartSet_WidgetBSplinePoints_H + +#include "PartSet.h" +#include "PartSet_MouseProcessor.h" + +#include + +#include + +class GeomAPI_Pnt2d; +class ModelAPI_CompositeFeature; +class ModuleBase_LabelValue; +class PartSet_ExternalObjectsMgr; +class QGroupBox; + +/**\class PartSet_WidgetBSplinePoints + * \ingroup Modules + * \brief Implementation of model widget to provide widget to input a list of 2D poles + * of B-spline curve in association with weights + * In XML can be defined as following: + * \code + * + * \endcode + */ +class PARTSET_EXPORT PartSet_WidgetBSplinePoints : public ModuleBase_ModelWidget, + public PartSet_MouseProcessor +{ +Q_OBJECT +public: + /// Constructor + /// \param theParent the parent object + /// \param theWorkshop a current workshop + /// \param theData the widget configuation. The attribute of the model widget is obtained from + PartSet_WidgetBSplinePoints(QWidget* theParent, ModuleBase_IWorkshop* theWorkshop, + const Config_WidgetAPI* theData); + /// Destructor + virtual ~PartSet_WidgetBSplinePoints(); + + /// Fills given container with selection modes if the widget has it + /// \param [out] theModuleSelectionModes module additional modes, -1 means all default modes + /// \param theModes [out] a container of modes + virtual void selectionModes(int& theModuleSelectionModes, QIntList& theModes); + + /// Checks if the selection presentation is valid in widget + /// \param theValue a selected presentation in the view + /// \return a boolean value + virtual bool isValidSelectionCustom(const std::shared_ptr& theValue); + + /// Checks all attribute validators returns valid. It tries on the given selection + /// to current attribute by setting the value inside and calling validators. After this, + /// the previous attribute value is restored.The valid/invalid value is cashed. + /// \param theValue a selected presentation in the view + /// \param theAttribute the attribute + /// \return a boolean value + bool isValidSelectionForAttribute_(const std::shared_ptr& theValue, + const std::shared_ptr& theAttribute); + + /// Fills the attribute with the value of the selected owner + /// \param thePrs a selected owner + bool setSelectionCustom(const std::shared_ptr& theValue); + + /// Returns list of widget controls + /// \return a control list + virtual QList getControls() const; + + /// The methiod called when widget is deactivated + virtual void deactivate(); + + /// \returns the sketch instance + std::shared_ptr sketch() const { return mySketch; } + + /// Set sketch instance + void setSketch(std::shared_ptr theSketch) { mySketch = theSketch; } + + /// Fill the widget values by given point + /// \param theX the X coordinate + /// \param theY the Y coordinate + /// \returns True in case of success + bool setPoint(double theX, double theY); + + /// Returns true if the event is processed. + virtual bool processEscape(); + + /// Returns true if the attribute can be changed using the selected shapes in the viewer + /// and creating a coincidence constraint to them. This control use them. + virtual bool useSelectedShapes() const; + + /// Processing the mouse move event in the viewer + /// \param theWindow a view window + /// \param theEvent a mouse event + virtual void mouseMoved(ModuleBase_IViewWindow* theWindow, QMouseEvent* theEvent); + + /// Processing the mouse release event in the viewer + /// \param theWindow a view window + /// \param theEvent a mouse event + virtual void mouseReleased(ModuleBase_IViewWindow* theWindow, QMouseEvent* theEvent); + +protected: + /// Saves the internal parameters to the given feature + /// \return True in success + virtual bool storeValueCustom(); + + /// Restore value from attribute data to the widget's control + virtual bool restoreValueCustom(); + + /// Store current value in cashed value + void storeCurentValue(); + + /// Restore cashed value in the model attribute + /// \return boolean state if the restored feature shoud be hidden + bool restoreCurentValue(); + + /// Fills the widget with default values + /// \return true if the widget current value is reset + virtual bool resetCustom(); + +private: + /// Create labels for the next B-spline point + void createNextPoint(); + /// Remove labels for the last B-spline point + void removeLastPoint(); + + /// Save B-spline poles and weights to corresponding attributes + void storePolesAndWeights() const; + + /// Returns attribute reference if the key is defined in XML definition of this control + /// \return found attribute or null + std::shared_ptr attributeRefAttrList() const; + + void fillRefAttribute(const std::shared_ptr& theValue); + void fillRefAttribute(std::shared_ptr theClickedPoint, + const std::shared_ptr& theValue); + void fillRefAttribute(const ObjectPtr& theObject); + + ObjectPtr getGeomSelection(const std::shared_ptr& theValue); + +protected: + ModuleBase_IWorkshop* myWorkshop; ///< workshop + +private: + QGroupBox* myGroupBox; ///< the parent group box for all intenal widgets + std::vector myXSpin; ///< the label for the X coordinate + std::vector myYSpin; ///< the label for the Y coordinate + std::vector myWeightSpin; ///< the label for the weight + PartSet_ExternalObjectsMgr* myExternalObjectMgr; ///< reference to external objects manager + + /// value used as selection in mouse release method + std::shared_ptr myPreSelected; + + /// it is important during restart operation + CompositeFeaturePtr mySketch; + + std::string myRefAttribute; /// if not empty, coincidences are not set but attribute is filled + + bool myValueIsCashed; /// boolean state if the value is cashed during value state change + bool myIsFeatureVisibleInCash; /// boolean value if the feature was visible when cash if filled + std::vector myXValueInCash; /// the cashed X value during value state change + std::vector myYValueInCash; /// the cashed Y value during value state change + std::vector myWeightInCash; /// the cached Weight value during valude state change + + std::string myWeightsAttr; + + int myPointIndex; /// index of the changing point + + bool myFinished; /// \c true if building the B-spline is finished (escape pressed) +}; + +#endif diff --git a/src/PartSet/PartSet_WidgetPoint2d.cpp b/src/PartSet/PartSet_WidgetPoint2d.cpp index 8183806b9..b01f5fecd 100644 --- a/src/PartSet/PartSet_WidgetPoint2d.cpp +++ b/src/PartSet/PartSet_WidgetPoint2d.cpp @@ -243,10 +243,10 @@ bool PartSet_WidgetPoint2D::setSelectionCustom(const ModuleBase_ViewerPrsPtr& th GeomShapePtr aShape = theValue->shape(); if (aShape.get() && !aShape->isNull()) { Handle(V3d_View) aView = myWorkshop->viewer()->activeView(); - double aX = 0, aY = 0; const TopoDS_Shape& aTDShape = aShape->impl(); - if (getPoint2d(aView, aTDShape, aX, aY)) { - fillRefAttribute(aX, aY, theValue); + GeomPnt2dPtr aPnt = PartSet_Tools::getPnt2d(aView, aTDShape, mySketch); + if (aPnt) { + fillRefAttribute(aPnt->x(), aPnt->y(), theValue); isDone = true; } else if (aTDShape.ShapeType() == TopAbs_EDGE) { @@ -306,11 +306,11 @@ bool PartSet_WidgetPoint2D::setSelection(QList& theValu GeomShapePtr aShape = aValue->shape(); if (aShape.get() && !aShape->isNull()) { Handle(V3d_View) aView = myWorkshop->viewer()->activeView(); - double aX = 0, aY = 0; const TopoDS_Shape& aTDShape = aShape->impl(); - if (getPoint2d(aView, aTDShape, aX, aY)) { - isDone = setPoint(aX, aY); - setConstraintToPoint(aX, aY, aValue); + GeomPnt2dPtr aPnt = PartSet_Tools::getPnt2d(aView, aTDShape, mySketch); + if (aPnt) { + isDone = setPoint(aPnt->x(), aPnt->y()); + setConstraintToPoint(aPnt->x(), aPnt->y(), aValue); } } } @@ -476,24 +476,6 @@ void PartSet_WidgetPoint2D::deactivate() ModuleBase_ModelWidget::deactivate(); } -bool PartSet_WidgetPoint2D::getPoint2d(const Handle(V3d_View)& theView, - const TopoDS_Shape& theShape, - double& theX, double& theY) const -{ - if (!theShape.IsNull()) { - if (theShape.ShapeType() == TopAbs_VERTEX) { - const TopoDS_Vertex& aVertex = TopoDS::Vertex(theShape); - if (!aVertex.IsNull()) { - // A case when point is taken from existing vertex - gp_Pnt aPoint = BRep_Tool::Pnt(aVertex); - PartSet_Tools::convertTo2D(aPoint, mySketch, theView, theX, theY); - return true; - } - } - } - return false; -} - bool PartSet_WidgetPoint2D::setConstraintToPoint(double theClickedX, double theClickedY, const std::shared_ptr& theValue) { @@ -627,14 +609,19 @@ void PartSet_WidgetPoint2D::mouseReleased(ModuleBase_IViewWindow* theWindow, QMo } } if (anExternal) { + GeomPnt2dPtr aPnt = PartSet_Tools::getPnt2d(aView, aShape, mySketch); double aX = 0, aY = 0; - if (getPoint2d(aView, aShape, aX, aY) && isFeatureContainsPoint(myFeature, aX, aY)) { + if (aPnt) { + aX = aPnt->x(); + aY = aPnt->y(); + } + if (aPnt && isFeatureContainsPoint(myFeature, aX, aY)) { // do not create a constraint to the point, which already used by the feature // if the feature contains the point, focus is not switched setPoint(aX, aY); } else { - if (getPoint2d(aView, aShape, aX, aY)) + if (aPnt) setPoint(aX, aY); else { if (aShape.ShapeType() == TopAbs_EDGE) { @@ -674,20 +661,23 @@ void PartSet_WidgetPoint2D::mouseReleased(ModuleBase_IViewWindow* theWindow, QMo } } if (!anExternal) { - double aX = 0, aY = 0; bool isProcessed = false; - if (getPoint2d(aView, aShape, aX, aY) && isFeatureContainsPoint(myFeature, aX, aY)) { + GeomPnt2dPtr aPnt = PartSet_Tools::getPnt2d(aView, aShape, mySketch); + if (aPnt && isFeatureContainsPoint(myFeature, aPnt->x(), aPnt->y())) { // when the point is selected, the coordinates of the point should be set into the attribute // if the feature contains the point, focus is not switched - setPoint(aX, aY); + setPoint(aPnt->x(), aPnt->y()); } else { + double aX = 0, aY = 0; bool anOrphanPoint = isOrphanPoint(aSelectedFeature, mySketch, aX, aY); // do not set a coincidence constraint in the attribute if the feature contains a point // with the same coordinates. It is important for line creation in order to do not set // the same constraints for the same points, oterwise the result line has zero length. bool isAuxiliaryFeature = false; - if (getPoint2d(aView, aShape, aX, aY)) { + if (aPnt) { + aX = aPnt->x(); + aY = aPnt->y(); setPoint(aX, aY); setConstraintToPoint(aX, aY, aFirstValue); } @@ -738,11 +728,11 @@ void PartSet_WidgetPoint2D::mouseReleased(ModuleBase_IViewWindow* theWindow, QMo // external objects e.g. selection of trihedron axis when input end arc point updateObject(feature()); - double aX = 0, aY = 0; - if (getPoint2d(aView, aShape, aX, aY)) { + GeomPnt2dPtr aPnt = PartSet_Tools::getPnt2d(aView, aShape, mySketch); + if (aPnt) { // do not create a constraint to the point, which already used by the feature // if the feature contains the point, focus is not switched - setPoint(aX, aY); + setPoint(aPnt->x(), aPnt->y()); } emit vertexSelected(); // it stops the reentrant operation emit focusOutWidget(this); diff --git a/src/PartSet/PartSet_WidgetPoint2d.h b/src/PartSet/PartSet_WidgetPoint2d.h index 515a03c41..3a062cfa4 100644 --- a/src/PartSet/PartSet_WidgetPoint2d.h +++ b/src/PartSet/PartSet_WidgetPoint2d.h @@ -211,14 +211,6 @@ protected: virtual void initializeValueByActivate(); private: - /// Returns point 2d from selected vertex - /// \param theView a view window - /// \param theShape a vertex shape - /// \param theX an output value of X coordinate - /// \param theY an output value of Y coordinate - bool getPoint2d(const Handle(V3d_View)& theView, const TopoDS_Shape& theShape, - double& theX, double& theY) const; - /// Creates constrains of the clicked point /// \param theClickedX the horizontal coordnate of the point /// \param theClickedY the vertical coordnate of the point @@ -230,6 +222,7 @@ protected: /// \return true if succed bool setConstraintToObject(const ObjectPtr& theObject); +public: /// Returns if the feature is an orphan point, circle or an arc. Returns true if it /// has no a coincident to other lines. It processes point, circle and arc features /// In circle an arc features, only centers are processed, for other points, it returns @@ -274,6 +267,7 @@ protected: const FeaturePtr& theSkipFeature, const std::shared_ptr& thePoint); +private: /// Returns attribute reference if the key is defined in XML definition of this control /// \return found attribute or null std::shared_ptr attributeRefAttr() const; diff --git a/src/SketchPlugin/CMakeLists.txt b/src/SketchPlugin/CMakeLists.txt index 10105e1ca..c282d5481 100644 --- a/src/SketchPlugin/CMakeLists.txt +++ b/src/SketchPlugin/CMakeLists.txt @@ -23,6 +23,7 @@ INCLUDE(UnitTest) SET(PROJECT_HEADERS SketchPlugin.h SketchPlugin_Arc.h + SketchPlugin_BSpline.h SketchPlugin_Circle.h SketchPlugin_Constraint.h SketchPlugin_ConstraintAngle.h @@ -54,6 +55,7 @@ SET(PROJECT_HEADERS SketchPlugin_Line.h SketchPlugin_MacroArc.h SketchPlugin_MacroArcReentrantMessage.h + SketchPlugin_MacroBSpline.h SketchPlugin_MacroCircle.h SketchPlugin_MacroEllipse.h SketchPlugin_MacroEllipticArc.h @@ -73,6 +75,7 @@ SET(PROJECT_HEADERS SET(PROJECT_SOURCES SketchPlugin_Arc.cpp + SketchPlugin_BSpline.cpp SketchPlugin_Circle.cpp SketchPlugin_Constraint.cpp SketchPlugin_ConstraintAngle.cpp @@ -102,6 +105,7 @@ SET(PROJECT_SOURCES SketchPlugin_IntersectionPoint.cpp SketchPlugin_Line.cpp SketchPlugin_MacroArc.cpp + SketchPlugin_MacroBSpline.cpp SketchPlugin_MacroCircle.cpp SketchPlugin_MacroEllipse.cpp SketchPlugin_MacroEllipticArc.cpp diff --git a/src/SketchPlugin/SketchPlugin_BSpline.cpp b/src/SketchPlugin/SketchPlugin_BSpline.cpp new file mode 100644 index 000000000..f226e0657 --- /dev/null +++ b/src/SketchPlugin/SketchPlugin_BSpline.cpp @@ -0,0 +1,152 @@ +// Copyright (C) 2019-2020 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 +#include + +#include + +////#include +////#include +////#include +////#include +#include +////#include + +#include + +#include +#include +#include +#include + +////#include +//// +////static const double tolerance = 1e-7; +////static const double paramTolerance = 1.e-4; +////static const double PI = 3.141592653589793238463; + + +SketchPlugin_BSpline::SketchPlugin_BSpline() + : SketchPlugin_SketchEntity() +{ +} + +void SketchPlugin_BSpline::initDerivedClassAttributes() +{ + data()->addAttribute(POLES_ID(), GeomDataAPI_Point2DArray::typeId()); + data()->addAttribute(WEIGHTS_ID(), ModelAPI_AttributeDoubleArray::typeId()); + + data()->addAttribute(EXTERNAL_ID(), ModelAPI_AttributeSelection::typeId()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), EXTERNAL_ID()); +} + +void SketchPlugin_BSpline::execute() +{ + SketchPlugin_Sketch* aSketch = sketch(); + if(!aSketch) { + return; + } + + AttributePoint2DArrayPtr aPolesArray = + std::dynamic_pointer_cast(attribute(POLES_ID())); + AttributeDoubleArrayPtr aWeightsArray = data()->realArray(WEIGHTS_ID()); + + // convert poles to 3D + std::vector aPoles3D; + aPoles3D.reserve(aPolesArray->size()); + for (int anIndex = 0; anIndex < aPolesArray->size(); ++anIndex) { + GeomPnt2dPtr aPole = aPolesArray->pnt(anIndex); + aPoles3D.push_back(aSketch->to3D(aPole->x(), aPole->y())); + } + // collect weights + std::vector aWeights; + aWeights.reserve(aWeightsArray->size()); + for (int anIndex = 0; anIndex < aWeightsArray->size(); ++anIndex) + aWeights.push_back(aWeightsArray->value(anIndex)); + + // create result non-periodic B-spline curve + GeomShapePtr anEdge = GeomAlgoAPI_EdgeBuilder::bspline(aPoles3D, aWeights, false); + + ResultConstructionPtr aResult = document()->createConstruction(data(), 0); + aResult->setShape(anEdge); + aResult->setIsInHistory(false); + setResult(aResult, 0); +} + +bool SketchPlugin_BSpline::isFixed() { + return data()->selection(EXTERNAL_ID())->context().get() != NULL; +} + +void SketchPlugin_BSpline::attributeChanged(const std::string& theID) { + // the second condition for unability to move external segments anywhere + if (theID == EXTERNAL_ID() || isFixed()) { + std::shared_ptr aSelection = data()->selection(EXTERNAL_ID())->value(); + if (!aSelection) { + // empty shape in selection shows that the shape is equal to context + ResultPtr anExtRes = selection(EXTERNAL_ID())->context(); + if (anExtRes) + aSelection = anExtRes->shape(); + } +//// // update arguments due to the selection value +//// if (aSelection && !aSelection->isNull() && aSelection->isEdge()) { +//// std::shared_ptr anEdge(new GeomAPI_Edge(aSelection)); +//// std::shared_ptr anEllipse = anEdge->ellipse(); +//// +//// bool aWasBlocked = data()->blockSendAttributeUpdated(true); +//// std::shared_ptr aCenterAttr = +//// std::dynamic_pointer_cast(attribute(CENTER_ID())); +//// aCenterAttr->setValue(sketch()->to2D(anEllipse->center())); +//// +//// std::shared_ptr aFocusAttr = +//// std::dynamic_pointer_cast(attribute(FIRST_FOCUS_ID())); +//// aFocusAttr->setValue(sketch()->to2D(anEllipse->firstFocus())); +//// +//// std::shared_ptr aStartAttr = +//// std::dynamic_pointer_cast(attribute(START_POINT_ID())); +//// aStartAttr->setValue(sketch()->to2D(anEdge->firstPoint())); +//// +//// std::shared_ptr aEndAttr = +//// std::dynamic_pointer_cast(attribute(END_POINT_ID())); +//// aEndAttr->setValue(sketch()->to2D(anEdge->lastPoint())); +//// +//// real(MAJOR_RADIUS_ID())->setValue(anEllipse->majorRadius()); +//// real(MINOR_RADIUS_ID())->setValue(anEllipse->minorRadius()); +//// +//// double aStartParam, aMidParam, aEndParam; +//// anEllipse->parameter(anEdge->firstPoint(), tolerance, aStartParam); +//// anEllipse->parameter(anEdge->middlePoint(), tolerance, aMidParam); +//// anEllipse->parameter(anEdge->lastPoint(), tolerance, aEndParam); +//// if (aEndParam < aStartParam) +//// aEndParam += 2.0 * PI; +//// if (aMidParam < aStartParam) +//// aMidParam += 2.0 * PI; +//// boolean(REVERSED_ID())->setValue(aMidParam > aEndParam); +//// +//// data()->blockSendAttributeUpdated(aWasBlocked, false); +//// +//// fillCharacteristicPoints(); +//// } + } +//// else if (theID == CENTER_ID() || theID == FIRST_FOCUS_ID() || +//// theID == START_POINT_ID() || theID == END_POINT_ID()) +//// fillCharacteristicPoints(); +//// else if (theID == REVERSED_ID() && myParamDelta == 0.0) +//// myParamDelta = 2.0 * PI; +} diff --git a/src/SketchPlugin/SketchPlugin_BSpline.h b/src/SketchPlugin/SketchPlugin_BSpline.h new file mode 100644 index 000000000..c918a090c --- /dev/null +++ b/src/SketchPlugin/SketchPlugin_BSpline.h @@ -0,0 +1,78 @@ +// Copyright (C) 2019-2020 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 +// + +#ifndef SketchPlugin_BSpline_H_ +#define SketchPlugin_BSpline_H_ + +#include +#include + +/**\class SketchPlugin_BSpline + * \ingroup Plugins + * \brief Feature for creation of the B-spline curve in the sketch. + */ +class SketchPlugin_BSpline : public SketchPlugin_SketchEntity +{ +public: + /// Ellipse feature kind + inline static const std::string& ID() + { + static const std::string ID("SketchBSpline"); + return ID; + } + + /// list of B-spline poles + inline static const std::string& POLES_ID() + { + static const std::string ID("poles"); + return ID; + } + + /// list of B-spline weights + inline static const std::string& WEIGHTS_ID() + { + static const std::string ID("weights"); + return ID; + } + + /// Returns the kind of a feature + SKETCHPLUGIN_EXPORT virtual const std::string& getKind() + { + static std::string MY_KIND = SketchPlugin_BSpline::ID(); + return MY_KIND; + } + + /// Returns true is sketch element is under the rigid constraint + SKETCHPLUGIN_EXPORT virtual bool isFixed(); + + /// Called on change of any argument-attribute of this object + SKETCHPLUGIN_EXPORT virtual void attributeChanged(const std::string& theID); + + /// Creates a new part document if needed + SKETCHPLUGIN_EXPORT virtual void execute(); + + /// Use plugin manager for features creation + SketchPlugin_BSpline(); + +protected: + /// \brief Initializes attributes of derived class. + virtual void initDerivedClassAttributes(); +}; + +#endif diff --git a/src/SketchPlugin/SketchPlugin_ConstraintCoincidenceInternal.cpp b/src/SketchPlugin/SketchPlugin_ConstraintCoincidenceInternal.cpp index ccab1cf93..07d5638ad 100644 --- a/src/SketchPlugin/SketchPlugin_ConstraintCoincidenceInternal.cpp +++ b/src/SketchPlugin/SketchPlugin_ConstraintCoincidenceInternal.cpp @@ -19,6 +19,10 @@ #include "SketchPlugin_ConstraintCoincidenceInternal.h" +#include +#include +#include + SketchPlugin_ConstraintCoincidenceInternal::SketchPlugin_ConstraintCoincidenceInternal() { } @@ -26,6 +30,12 @@ SketchPlugin_ConstraintCoincidenceInternal::SketchPlugin_ConstraintCoincidenceIn void SketchPlugin_ConstraintCoincidenceInternal::initAttributes() { SketchPlugin_ConstraintCoincidence::initAttributes(); + + data()->addAttribute(INDEX_ENTITY_A(), ModelAPI_AttributeInteger::typeId()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), INDEX_ENTITY_A()); + + data()->addAttribute(INDEX_ENTITY_B(), ModelAPI_AttributeInteger::typeId()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), INDEX_ENTITY_B()); } void SketchPlugin_ConstraintCoincidenceInternal::execute() diff --git a/src/SketchPlugin/SketchPlugin_ConstraintCoincidenceInternal.h b/src/SketchPlugin/SketchPlugin_ConstraintCoincidenceInternal.h index fac0060eb..d5a65f810 100644 --- a/src/SketchPlugin/SketchPlugin_ConstraintCoincidenceInternal.h +++ b/src/SketchPlugin/SketchPlugin_ConstraintCoincidenceInternal.h @@ -30,7 +30,7 @@ class SketchPlugin_ConstraintCoincidenceInternal : public SketchPlugin_ConstraintCoincidence { public: - /// Coincidence constraint kind + /// \brief Coincidence constraint kind inline static const std::string& ID() { static const std::string MY_CONSTRAINT_COINCIDENCE_ID("SketchConstraintCoincidenceInternal"); @@ -43,7 +43,20 @@ class SketchPlugin_ConstraintCoincidenceInternal : public SketchPlugin_Constrain return MY_KIND; } - /// Returns the AIS preview + /// \brief Index of point in the array if the first attribute is an array + inline static const std::string& INDEX_ENTITY_A() + { + static const std::string MY_INDEX("ConstraintEntityA_Index"); + return MY_INDEX; + } + /// \brief Index of point in the array if the second attribute is an array + inline static const std::string& INDEX_ENTITY_B() + { + static const std::string MY_INDEX("ConstraintEntityB_Index"); + return MY_INDEX; + } + + /// \brief Returns the AIS preview SKETCHPLUGIN_EXPORT virtual AISObjectPtr getAISObject(AISObjectPtr thePrevious); /// \brief Creates a new part document if needed diff --git a/src/SketchPlugin/SketchPlugin_MacroBSpline.cpp b/src/SketchPlugin/SketchPlugin_MacroBSpline.cpp new file mode 100644 index 000000000..ee47982d8 --- /dev/null +++ b/src/SketchPlugin/SketchPlugin_MacroBSpline.cpp @@ -0,0 +1,333 @@ +// Copyright (C) 2019-2020 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 + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#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 +static void createInternalConstraint(SketchPlugin_Sketch* theSketch, + AttributePtr thePoint, + AttributePtr theBSplinePoles, + const int thePoleIndex); + + +SketchPlugin_MacroBSpline::SketchPlugin_MacroBSpline() + : SketchPlugin_SketchEntity(), + myIsPeriodic(false) +{ +} + +void SketchPlugin_MacroBSpline::initAttributes() +{ + data()->addAttribute(POLES_ID(), GeomDataAPI_Point2DArray::typeId()); + data()->addAttribute(WEIGHTS_ID(), ModelAPI_AttributeDoubleArray::typeId()); + + data()->addAttribute(REF_POLES_ID(), ModelAPI_AttributeRefAttrList::typeId()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), REF_POLES_ID()); + + data()->addAttribute(AUXILIARY_ID(), ModelAPI_AttributeBoolean::typeId()); +} + +void SketchPlugin_MacroBSpline::execute() +{ + FeaturePtr aBSpline = createBSplineFeature(); + + std::list aControlPoles; + createControlPolygon(aBSpline, aControlPoles); + constraintsForPoles(aControlPoles); + + // message to init reentrant operation + static Events_ID anId = ModelAPI_EventReentrantMessage::eventId(); + ReentrantMessagePtr aMessage(new ModelAPI_EventReentrantMessage(anId, this)); + // set here the last pole to make coincidence with the start point of the next B-spline curve + aMessage->setCreatedFeature(aControlPoles.back()); + Events_Loop::loop()->send(aMessage); +} + +// LCOV_EXCL_START +std::string SketchPlugin_MacroBSpline::processEvent( + const std::shared_ptr& theMessage) +{ + ReentrantMessagePtr aReentrantMessage = + std::dynamic_pointer_cast(theMessage); + if (aReentrantMessage) { + FeaturePtr aCreatedFeature = aReentrantMessage->createdFeature(); + ObjectPtr anObject = aReentrantMessage->selectedObject(); + AttributePtr anAttribute = aReentrantMessage->selectedAttribute(); + std::shared_ptr aClickedPoint = aReentrantMessage->clickedPoint(); + + if (aClickedPoint) { + // fill points list (it consists of 2 points to make editable the second one) + AttributePoint2DArrayPtr aPointArrayAttr = + std::dynamic_pointer_cast(attribute(POLES_ID())); + aPointArrayAttr->setSize(2); + aPointArrayAttr->setPnt(0, aClickedPoint); + aPointArrayAttr->setPnt(1, aClickedPoint); + + // fill weights + AttributeDoubleArrayPtr aWeightsArrayAttr = data()->realArray(WEIGHTS_ID()); + aWeightsArrayAttr->setSize(2); + aWeightsArrayAttr->setValue(0, 1.0); + aWeightsArrayAttr->setValue(1, 1.0); + + // fill reference attribute + AttributeRefAttrListPtr aRefAttrList = + std::dynamic_pointer_cast(attribute(REF_POLES_ID())); + if (anAttribute) { + if (!anAttribute->owner() || !anAttribute->owner()->data()->isValid()) { + if (aCreatedFeature && anAttribute->id() == SketchPlugin_Point::COORD_ID()) + anAttribute = aCreatedFeature->attribute(SketchPlugin_Point::COORD_ID()); + } + aRefAttrList->append(anAttribute); + } + } + Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED)); + } + return std::string(); +} +// LCOV_EXCL_STOP + +FeaturePtr SketchPlugin_MacroBSpline::createBSplineFeature() +{ + FeaturePtr aBSpline = sketch()->addFeature(SketchPlugin_BSpline::ID()); + + AttributePoint2DArrayPtr aPoles = std::dynamic_pointer_cast( + aBSpline->attribute(SketchPlugin_BSpline::POLES_ID())); + AttributePoint2DArrayPtr aPolesMacro = + std::dynamic_pointer_cast(attribute(POLES_ID())); + aPoles->assign(aPolesMacro); + + AttributeDoubleArrayPtr aWeights = + aBSpline->data()->realArray(SketchPlugin_BSpline::WEIGHTS_ID()); + AttributeDoubleArrayPtr aWeightsMacro = data()->realArray(WEIGHTS_ID()); + int aSize = aWeightsMacro->size(); + aWeights->setSize(aSize); + for (int index = 0; index < aSize; ++index) + aWeights->setValue(index, aWeightsMacro->value(index)); + + aBSpline->boolean(SketchPlugin_BSpline::AUXILIARY_ID())->setValue( + boolean(AUXILIARY_ID())->value()); + + aBSpline->execute(); + + return aBSpline; +} + +void SketchPlugin_MacroBSpline::createControlPolygon(FeaturePtr theBSpline, + std::list& thePoles) +{ + AttributePoint2DArrayPtr aPoles = std::dynamic_pointer_cast( + theBSpline->attribute(SketchPlugin_BSpline::POLES_ID())); + int aSize = aPoles->size(); + // poles + for (int index = 0; index < aSize; ++index) + thePoles.push_back(createAuxiliaryPole(theBSpline, aPoles, index)); + // segments + for (int index = 1; index < aSize; ++index) + createAuxiliarySegment(theBSpline, aPoles, index - 1, index); +} + +void SketchPlugin_MacroBSpline::constraintsForPoles(const std::list& thePoles) +{ + AttributeRefAttrListPtr aRefAttrList = data()->refattrlist(REF_POLES_ID()); + std::list > aList; + if (aRefAttrList) + aList = aRefAttrList->list(); + + SketchPlugin_Sketch* aSketch = sketch(); + + std::list >::iterator aLIt = aList.begin(); + std::list::const_iterator aPIt = thePoles.begin(); + for (; aLIt != aList.end() && aPIt != thePoles.end(); ++aPIt, ++aLIt) { + // firstly, check the attribute (in this case the object will be not empty too) + if (aLIt->second) { + SketchPlugin_Tools::createConstraintAttrAttr(aSketch, + SketchPlugin_ConstraintCoincidence::ID(), + (*aPIt)->attribute(SketchPlugin_Point::COORD_ID()), aLIt->second); + } + // now add coincidence with the result + else if (aLIt->first) { + SketchPlugin_Tools::createConstraintAttrObject(aSketch, + SketchPlugin_ConstraintCoincidence::ID(), + (*aPIt)->attribute(SketchPlugin_Point::COORD_ID()), aLIt->first); + } + } +} + +AISObjectPtr SketchPlugin_MacroBSpline::getAISObject(AISObjectPtr thePrevious) +{ + SketchPlugin_Sketch* aSketch = sketch(); + if (!aSketch) + return AISObjectPtr(); + + AttributePoint2DArrayPtr aPolesArray = + std::dynamic_pointer_cast(attribute(POLES_ID())); + AttributeDoubleArrayPtr aWeightsArray = data()->realArray(WEIGHTS_ID()); + + std::list aShapes; + + // convert poles to 3D and collect weights + std::vector aPoles3D; + aPoles3D.reserve(aPolesArray->size()); + std::vector aWeights; + aWeights.reserve(aWeightsArray->size()); + for (int anIndex = 0; anIndex < aPolesArray->size(); ++anIndex) { + double aWeight = aWeightsArray->value(anIndex); + if (aWeight < 1.e-10) + continue; // skip poles with zero weights + + aWeights.push_back(aWeight); + + GeomPnt2dPtr aPole = aPolesArray->pnt(anIndex); + GeomPointPtr aPole3D = aSketch->to3D(aPole->x(), aPole->y()); + aPoles3D.push_back(aPole3D); + aShapes.push_back(GeomAlgoAPI_PointBuilder::vertex(aPole3D)); + } + + // create result non-periodic B-spline curve + GeomShapePtr anEdge = GeomAlgoAPI_EdgeBuilder::bspline(aPoles3D, aWeights, false); + if (!anEdge) + return AISObjectPtr(); + + aShapes.push_back(anEdge); + GeomShapePtr aCompound = GeomAlgoAPI_CompoundBuilder::compound(aShapes); + + AISObjectPtr anAIS = thePrevious; + if (!anAIS) + anAIS.reset(new GeomAPI_AISObject()); + anAIS->createShape(aCompound); + + // Modify attributes + SketchPlugin_Tools::customizeFeaturePrs(anAIS, boolean(AUXILIARY_ID())->value()); + + return anAIS; +} + + + +// ========================== Auxiliary functions =========================================== + +FeaturePtr createAuxiliaryPole(FeaturePtr theBSpline, + AttributePoint2DArrayPtr theBSplinePoles, + const int thePoleIndex) +{ + SketchPlugin_Sketch* aSketch = + std::dynamic_pointer_cast(theBSpline)->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); + + GeomPnt2dPtr aPole = theBSplinePoles->pnt(thePoleIndex); + + AttributePoint2DPtr aCoord = std::dynamic_pointer_cast( + aPointFeature->attribute(SketchPlugin_Point::COORD_ID())); + aCoord->setValue(aPole); + + aPointFeature->execute(); + + std::ostringstream aName; + aName << theBSpline->name() << "_" << theBSplinePoles->id() << "_" << thePoleIndex; + aPointFeature->data()->setName(aName.str()); + aPointFeature->lastResult()->data()->setName(aName.str()); + + // internal constraint to keep position of the point + createInternalConstraint(aSketch, aCoord, theBSplinePoles, thePoleIndex); + + return aPointFeature; +} + +void createAuxiliarySegment(FeaturePtr theBSpline, + AttributePoint2DArrayPtr theBSplinePoles, + const int thePoleIndex1, + const int thePoleIndex2) +{ + SketchPlugin_Sketch* aSketch = + std::dynamic_pointer_cast(theBSpline)->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); + + AttributePoint2DPtr aLineStart = std::dynamic_pointer_cast( + aLineFeature->attribute(SketchPlugin_Line::START_ID())); + aLineStart->setValue(theBSplinePoles->pnt(thePoleIndex1)); + + AttributePoint2DPtr aLineEnd = std::dynamic_pointer_cast( + aLineFeature->attribute(SketchPlugin_Line::END_ID())); + aLineEnd->setValue(theBSplinePoles->pnt(thePoleIndex2)); + + aLineFeature->execute(); + + std::ostringstream aName; + aName << theBSpline->name() << "_segment_" << thePoleIndex1 << "_" << thePoleIndex2; + aLineFeature->data()->setName(aName.str()); + aLineFeature->lastResult()->data()->setName(aName.str()); + + // internal constraints to keep the segment position + createInternalConstraint(aSketch, aLineStart, theBSplinePoles, thePoleIndex1); + createInternalConstraint(aSketch, aLineEnd, theBSplinePoles, thePoleIndex2); +} + +void createInternalConstraint(SketchPlugin_Sketch* theSketch, + AttributePtr thePoint, + AttributePtr theBSplinePoles, + const int thePoleIndex) +{ + std::shared_ptr aConstraint = + std::dynamic_pointer_cast( + theSketch->addFeature(SketchPlugin_ConstraintCoincidenceInternal::ID())); + aConstraint->refattr(SketchPlugin_Constraint::ENTITY_A())->setAttr(thePoint); + aConstraint->refattr(SketchPlugin_Constraint::ENTITY_B())->setAttr(theBSplinePoles); + aConstraint->integer(SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B()) + ->setValue(thePoleIndex); +} diff --git a/src/SketchPlugin/SketchPlugin_MacroBSpline.h b/src/SketchPlugin/SketchPlugin_MacroBSpline.h new file mode 100644 index 000000000..f74b5bb50 --- /dev/null +++ b/src/SketchPlugin/SketchPlugin_MacroBSpline.h @@ -0,0 +1,110 @@ +// Copyright (C) 2019-2020 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 +// + +#ifndef SketchPlugin_MacroBSpline_H_ +#define SketchPlugin_MacroBSpline_H_ + +#include + +#include "SketchPlugin.h" + +#include "SketchPlugin_SketchEntity.h" + +#include + +class GeomAPI_Circ2d; +class GeomAPI_Pnt2d; + +/**\class SketchPlugin_MacroBSpline + * \ingroup Plugins + * \brief Feature for creation of the new B-spline in Sketch. + */ +class SketchPlugin_MacroBSpline : public SketchPlugin_SketchEntity, + public GeomAPI_IPresentable, + public ModelAPI_IReentrant +{ +public: + /// B-spline macro feature kind + inline static const std::string& ID() + { + static const std::string ID("SketchMacroBSpline"); + return ID; + } + + + /// list of B-spline poles + inline static const std::string& POLES_ID() + { + static const std::string ID("poles"); + return ID; + } + + /// list of references of B-spline poles + inline static const std::string& REF_POLES_ID() + { + static const std::string ID("poles_ref"); + return ID; + } + + /// list of B-spline weights + inline static const std::string& WEIGHTS_ID() + { + static const std::string ID("weights"); + return ID; + } + + /// Returns the kind of a feature + SKETCHPLUGIN_EXPORT virtual const std::string& getKind() + { + static std::string MY_KIND = SketchPlugin_MacroBSpline::ID(); + return MY_KIND; + } + + /// \brief Request for initialization of data model of the feature: adding all attributes. + SKETCHPLUGIN_EXPORT virtual void initAttributes(); + + /// Returns the AIS preview + virtual AISObjectPtr getAISObject(AISObjectPtr thePrevious); + + /// Creates a new part document if needed + SKETCHPLUGIN_EXPORT virtual void execute(); + + /// Reimplemented from ModelAPI_Feature::isMacro(). + /// \returns true + SKETCHPLUGIN_EXPORT virtual bool isMacro() const {return true;}; + + SKETCHPLUGIN_EXPORT virtual bool isPreviewNeeded() const {return false;}; + + /// Apply information of the message to current object. It fills reference object, + /// tangent type and tangent point refence in case of tangent arc + virtual std::string processEvent(const std::shared_ptr& theMessage); + + /// Use plugin manager for features creation + SketchPlugin_MacroBSpline(); + +private: + FeaturePtr createBSplineFeature(); + + void createControlPolygon(FeaturePtr theBSpline, std::list& thePoles); + void constraintsForPoles(const std::list& thePoles); + + bool myIsPeriodic; +}; + +#endif diff --git a/src/SketchPlugin/SketchPlugin_Plugin.cpp b/src/SketchPlugin/SketchPlugin_Plugin.cpp index ac1b442c8..a6ef2a190 100644 --- a/src/SketchPlugin/SketchPlugin_Plugin.cpp +++ b/src/SketchPlugin/SketchPlugin_Plugin.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -147,6 +149,8 @@ SketchPlugin_Plugin::SketchPlugin_Plugin() new SketchPlugin_SketchFeatureValidator); aFactory->registerValidator("SketchPlugin_MultiRotationAngleValidator", new SketchPlugin_MultiRotationAngleValidator); + aFactory->registerValidator("SketchPlugin_BSplineValidator", + new SketchPlugin_BSplineValidator); // register this plugin ModelAPI_Session::get()->registerPlugin(this); @@ -201,6 +205,8 @@ FeaturePtr SketchPlugin_Plugin::createFeature(std::string theFeatureID) return FeaturePtr(new SketchPlugin_Circle); } else if (theFeatureID == SketchPlugin_Arc::ID()) { return FeaturePtr(new SketchPlugin_Arc); + } else if (theFeatureID == SketchPlugin_BSpline::ID()) { + return FeaturePtr(new SketchPlugin_BSpline); } else if (theFeatureID == SketchPlugin_Projection::ID()) { return FeaturePtr(new SketchPlugin_Projection); } else if (theFeatureID == SketchPlugin_ConstraintCoincidence::ID()) { @@ -251,6 +257,8 @@ FeaturePtr SketchPlugin_Plugin::createFeature(std::string theFeatureID) return FeaturePtr(new SketchPlugin_Trim); } else if (theFeatureID == SketchPlugin_MacroArc::ID()) { return FeaturePtr(new SketchPlugin_MacroArc); + } else if (theFeatureID == SketchPlugin_MacroBSpline::ID()) { + return FeaturePtr(new SketchPlugin_MacroBSpline); } else if (theFeatureID == SketchPlugin_MacroCircle::ID()) { return FeaturePtr(new SketchPlugin_MacroCircle); } else if (theFeatureID == SketchPlugin_Ellipse::ID()) { @@ -306,6 +314,7 @@ std::shared_ptr SketchPlugin_Plugin aMsg->setState(SketchPlugin_Line::ID(), aHasSketchPlane); aMsg->setState(SketchPlugin_Circle::ID(), aHasSketchPlane); aMsg->setState(SketchPlugin_Arc::ID(), aHasSketchPlane); + aMsg->setState(SketchPlugin_BSpline::ID(), aHasSketchPlane); aMsg->setState(SketchPlugin_Ellipse::ID(), aHasSketchPlane); aMsg->setState(SketchPlugin_EllipticArc::ID(), aHasSketchPlane); aMsg->setState(SketchPlugin_Projection::ID(), aHasSketchPlane); @@ -331,6 +340,7 @@ std::shared_ptr SketchPlugin_Plugin aMsg->setState(SketchPlugin_Split::ID(), aHasSketchPlane); aMsg->setState(SketchPlugin_Trim::ID(), aHasSketchPlane); aMsg->setState(SketchPlugin_MacroArc::ID(), aHasSketchPlane); + aMsg->setState(SketchPlugin_MacroBSpline::ID(), aHasSketchPlane); aMsg->setState(SketchPlugin_MacroCircle::ID(), aHasSketchPlane); aMsg->setState(SketchPlugin_MacroEllipse::ID(), aHasSketchPlane); aMsg->setState(SketchPlugin_MacroEllipticArc::ID(), aHasSketchPlane); diff --git a/src/SketchPlugin/SketchPlugin_Validators.cpp b/src/SketchPlugin/SketchPlugin_Validators.cpp index 8e1bccbf6..a974f7437 100644 --- a/src/SketchPlugin/SketchPlugin_Validators.cpp +++ b/src/SketchPlugin/SketchPlugin_Validators.cpp @@ -65,7 +65,9 @@ #include #include #include + #include +#include #include #include @@ -1779,3 +1781,20 @@ bool SketchPlugin_MultiRotationAngleValidator::isValid(const AttributePtr& theAt return true; } + +bool SketchPlugin_BSplineValidator::isValid(const AttributePtr& theAttribute, + const std::list& theArguments, + Events_InfoMessage& theError) const +{ + AttributePoint2DArrayPtr aPolesArray = + std::dynamic_pointer_cast(theAttribute); + if (!aPolesArray) + return false; + + if (aPolesArray->size() < 2) { + theError = "Number of B-spline poles should be 2 and more"; + return false; + } + + return true; +} diff --git a/src/SketchPlugin/SketchPlugin_Validators.h b/src/SketchPlugin/SketchPlugin_Validators.h index b3fe8f574..1b20c45fd 100644 --- a/src/SketchPlugin/SketchPlugin_Validators.h +++ b/src/SketchPlugin/SketchPlugin_Validators.h @@ -531,4 +531,19 @@ class SketchPlugin_MultiRotationAngleValidator : public ModelAPI_AttributeValida Events_InfoMessage& theError) const; }; +/**\class SketchPlugin_BSplineValidator + * \ingroup Validators + * \brief Validator for checking poles/weights of B-spline curve. + */ +class SketchPlugin_BSplineValidator : public ModelAPI_AttributeValidator +{ + //! returns true if attribute is valid + //! \param theAttribute the checked attribute + //! \param theArguments arguments of the attribute + //! \param theError error message + virtual bool isValid(const AttributePtr& theAttribute, + const std::list& theArguments, + Events_InfoMessage& theError) const; +}; + #endif diff --git a/src/SketchPlugin/icons/bspline.png b/src/SketchPlugin/icons/bspline.png new file mode 100644 index 000000000..149d19ee0 Binary files /dev/null and b/src/SketchPlugin/icons/bspline.png differ diff --git a/src/SketchPlugin/icons/bspline_p.png b/src/SketchPlugin/icons/bspline_p.png new file mode 100644 index 000000000..a23ade191 Binary files /dev/null and b/src/SketchPlugin/icons/bspline_p.png differ diff --git a/src/SketchPlugin/plugin-Sketch.xml b/src/SketchPlugin/plugin-Sketch.xml index 0c77223de..2f3ee2761 100644 --- a/src/SketchPlugin/plugin-Sketch.xml +++ b/src/SketchPlugin/plugin-Sketch.xml @@ -6,6 +6,7 @@ nested="SketchPoint SketchIntersectionPoint SketchLine SketchCircle SketchMacroCircle SketchArc SketchMacroArc SketchEllipse SketchMacroEllipse SketchEllipticArc SketchMacroEllipticArc + SketchBSpline SketchMacroBSpline SketchRectangle SketchProjection SketchConstraintLength SketchConstraintRadius SketchConstraintDistance SketchConstraintDistanceHorizontal SketchConstraintDistanceVertical @@ -596,6 +597,52 @@ + + + + + + + + + + + + + + + + + + #include +#include #include #include +#include #include +#include #include +#include #include +#include #include #include @@ -80,6 +85,40 @@ static EntityWrapperPtr createScalar(const AttributePtr& theAttribute, return aWrapper; } +static EntityWrapperPtr createScalarArray(const AttributePtr& theAttribute, + PlaneGCSSolver_Storage* theStorage) +{ + AttributeDoubleArrayPtr anArray = + std::dynamic_pointer_cast(theAttribute); + if (!anArray || !anArray->isInitialized()) + return EntityWrapperPtr(); + + int aSize = anArray->size(); + GCS::VEC_pD aParameters; + aParameters.reserve(aSize); + for (int index = 0; index < aSize; ++index) { + double* aParam = createParameter(theStorage); + *aParam = anArray->value(index); + aParameters.push_back(aParam); + } + + return EntityWrapperPtr(new PlaneGCSSolver_ScalarArrayWrapper(aParameters)); +} + +static PointWrapperPtr createPoint(const GeomPnt2dPtr& thePoint, PlaneGCSSolver_Storage* theStorage) +{ + GCSPointPtr aNewPoint(new GCS::Point); + + aNewPoint->x = createParameter(theStorage); + aNewPoint->y = createParameter(theStorage); + if (thePoint) { + *(aNewPoint->x) = thePoint->x(); + *(aNewPoint->y) = thePoint->y(); + } + + return PointWrapperPtr(new PlaneGCSSolver_PointWrapper(aNewPoint)); +} + static EntityWrapperPtr createPoint(const AttributePtr& theAttribute, PlaneGCSSolver_Storage* theStorage) { @@ -88,16 +127,29 @@ static EntityWrapperPtr createPoint(const AttributePtr& theAttribute, if (!aPoint2D) return EntityWrapperPtr(); - GCSPointPtr aNewPoint(new GCS::Point); + GeomPnt2dPtr aPnt; + if (aPoint2D->isInitialized()) + aPnt = aPoint2D->pnt(); - aNewPoint->x = createParameter(theStorage); - aNewPoint->y = createParameter(theStorage); - if (aPoint2D->isInitialized()) { - *(aNewPoint->x) = aPoint2D->x(); - *(aNewPoint->y) = aPoint2D->y(); - } + return createPoint(aPnt, theStorage); +} - return EntityWrapperPtr(new PlaneGCSSolver_PointWrapper(aNewPoint)); +static EntityWrapperPtr createPointArray(const AttributePtr& theAttribute, + PlaneGCSSolver_Storage* theStorage) +{ + std::shared_ptr aPointArray = + std::dynamic_pointer_cast(theAttribute); + if (!aPointArray) + return EntityWrapperPtr(); + + int aSize = aPointArray->size(); + + std::vector aPointWrappers; + aPointWrappers.reserve(aSize); + for (int index = 0; index < aSize; ++index) + aPointWrappers.push_back(createPoint(aPointArray->pnt(index), theStorage)); + + return EntityWrapperPtr(new PlaneGCSSolver_PointArrayWrapper(aPointWrappers)); } EntityWrapperPtr PlaneGCSSolver_AttributeBuilder::createAttribute( @@ -112,6 +164,10 @@ EntityWrapperPtr PlaneGCSSolver_AttributeBuilder::createAttribute( aResult = createScalar(theAttribute, myStorage); if (!aResult) aResult = createBoolean(theAttribute); + if (!aResult) + aResult = createPointArray(theAttribute, myStorage); + if (!aResult) + aResult = createScalarArray(theAttribute, myStorage); if (aResult && !myStorage) aResult->setExternal(true); return aResult; diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Defs.h b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Defs.h index 0f46e7544..eea27bf06 100644 --- a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Defs.h +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Defs.h @@ -46,13 +46,16 @@ enum SketchSolver_EntityType { ENTITY_UNKNOWN = 0, ENTITY_BOOLEAN, ENTITY_SCALAR, + ENTITY_SCALAR_ARRAY, ENTITY_ANGLE, ENTITY_POINT, + ENTITY_POINT_ARRAY, ENTITY_LINE, ENTITY_CIRCLE, ENTITY_ARC, ENTITY_ELLIPSE, - ENTITY_ELLIPTIC_ARC + ENTITY_ELLIPTIC_ARC, + ENTITY_BSPLINE }; /// Types of constraints diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_EdgeWrapper.cpp b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_EdgeWrapper.cpp index f96832038..669aece33 100644 --- a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_EdgeWrapper.cpp +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_EdgeWrapper.cpp @@ -20,45 +20,28 @@ #include #include -static bool isLine(const GCSCurvePtr& theEntity) +template +static bool isCurve(const GCSCurvePtr& theEntity) { - return std::dynamic_pointer_cast(theEntity).get(); -} - -static bool isCircle(const GCSCurvePtr& theEntity) -{ - return std::dynamic_pointer_cast(theEntity).get(); -} - -static bool isArc(const GCSCurvePtr& theEntity) -{ - return std::dynamic_pointer_cast(theEntity).get(); -} - -static bool isEllipse(const GCSCurvePtr& theEntity) -{ - return std::dynamic_pointer_cast(theEntity).get(); -} - -static bool isEllipticArc(const GCSCurvePtr& theEntity) -{ - return std::dynamic_pointer_cast(theEntity).get(); + return std::dynamic_pointer_cast(theEntity).get(); } PlaneGCSSolver_EdgeWrapper::PlaneGCSSolver_EdgeWrapper(const GCSCurvePtr theEntity) : myEntity(theEntity) { - if (isLine(myEntity)) + if (isCurve(myEntity)) myType = ENTITY_LINE; - else if (isArc(myEntity)) + else if (isCurve(myEntity)) myType = ENTITY_ARC; - else if (isCircle(myEntity)) + else if (isCurve(myEntity)) myType = ENTITY_CIRCLE; - else if (isEllipticArc(myEntity)) + else if (isCurve(myEntity)) myType = ENTITY_ELLIPTIC_ARC; - else if (isEllipse(myEntity)) + else if (isCurve(myEntity)) myType = ENTITY_ELLIPSE; + else if (isCurve(myEntity)) + myType = ENTITY_BSPLINE; } static double squareDistance(const GCS::Point& theP1, const GCS::Point& theP2) diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_FeatureBuilder.cpp b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_FeatureBuilder.cpp index 400319aed..78dc0d7c9 100644 --- a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_FeatureBuilder.cpp +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_FeatureBuilder.cpp @@ -20,11 +20,14 @@ #include #include #include +#include #include +#include #include #include #include +#include #include #include #include @@ -46,6 +49,7 @@ static EntityWrapperPtr createArc(const AttributeEntityMap& theAttributes, static EntityWrapperPtr createEllipse(const AttributeEntityMap& theAttributes); static EntityWrapperPtr createEllipticArc(const AttributeEntityMap& theAttributes, PlaneGCSSolver_Storage* theStorage); +static EntityWrapperPtr createBSpline(const AttributeEntityMap& theAttributes); PlaneGCSSolver_FeatureBuilder::PlaneGCSSolver_FeatureBuilder( @@ -102,6 +106,9 @@ EntityWrapperPtr PlaneGCSSolver_FeatureBuilder::createFeature(FeaturePtr theFeat // Arc of ellipse else if (aFeatureKind == SketchPlugin_EllipticArc::ID()) aResult = createEllipticArc(myAttributes, myStorage); + // B-spline curve + else if (aFeatureKind == SketchPlugin_BSpline::ID()) + aResult = createBSpline(myAttributes); // Point (it has low probability to be an attribute of constraint, so it is checked at the end) else if (aFeatureKind == SketchPlugin_Point::ID() || aFeatureKind == SketchPlugin_IntersectionPoint::ID()) { @@ -288,6 +295,38 @@ EntityWrapperPtr createEllipticArc(const AttributeEntityMap& theAttributes, return anEllipseWrapper; } +EntityWrapperPtr createBSpline(const AttributeEntityMap& theAttributes) +{ + std::shared_ptr aNewSpline(new GCS::BSpline); + + aNewSpline->degree = 3; + aNewSpline->periodic = false; + + AttributeEntityMap::const_iterator anIt = theAttributes.begin(); + for (; anIt != theAttributes.end(); ++anIt) { + const std::string& anAttrID = anIt->first->id(); + if (anAttrID == SketchPlugin_BSpline::POLES_ID()) { + PointArrayWrapperPtr anArray = + std::dynamic_pointer_cast(anIt->second); + + int aSize = anArray->size(); + aNewSpline->poles.reserve(aSize); + for (int anIndex = 0; anIndex < aSize; ++anIndex) + aNewSpline->poles.push_back(*anArray->value(anIndex)->point()); + + aNewSpline->start = aNewSpline->poles.front(); + aNewSpline->end = aNewSpline->poles.back(); + } + else if (anAttrID == SketchPlugin_BSpline::WEIGHTS_ID()) { + ScalarArrayWrapperPtr anArray = + std::dynamic_pointer_cast(anIt->second); + aNewSpline->weights = anArray->array(); + } + } + + return EdgeWrapperPtr(new PlaneGCSSolver_EdgeWrapper(aNewSpline)); +} + bool isAttributeApplicable(const std::string& theAttrName, const std::string& theOwnerName) { if (theOwnerName == SketchPlugin_Arc::ID()) { @@ -329,6 +368,10 @@ bool isAttributeApplicable(const std::string& theAttrName, const std::string& th theAttrName == SketchPlugin_EllipticArc::END_POINT_ID() || theAttrName == SketchPlugin_EllipticArc::REVERSED_ID(); } + else if (theOwnerName == SketchPlugin_BSpline::ID()) { + return theAttrName == SketchPlugin_BSpline::POLES_ID() || + theAttrName == SketchPlugin_BSpline::WEIGHTS_ID(); + } // suppose that all remaining features are points return theAttrName == SketchPlugin_Point::COORD_ID(); diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_PointArrayWrapper.cpp b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_PointArrayWrapper.cpp new file mode 100644 index 000000000..33a7954df --- /dev/null +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_PointArrayWrapper.cpp @@ -0,0 +1,26 @@ +// Copyright (C) 2019-2020 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 + +PlaneGCSSolver_PointArrayWrapper::PlaneGCSSolver_PointArrayWrapper( + const std::vector& thePoints) + : myPoints(thePoints) +{ +} diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_PointArrayWrapper.h b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_PointArrayWrapper.h new file mode 100644 index 000000000..8752b79c9 --- /dev/null +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_PointArrayWrapper.h @@ -0,0 +1,51 @@ +// Copyright (C) 2019-2020 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 +// + +#ifndef PlaneGCSSolver_PointArrayWrapper_H_ +#define PlaneGCSSolver_PointArrayWrapper_H_ + +#include +#include + +/** + * Wrapper providing operations with arrays of PlaneGCS points. + */ +class PlaneGCSSolver_PointArrayWrapper : public PlaneGCSSolver_EntityWrapper +{ +public: + PlaneGCSSolver_PointArrayWrapper(const std::vector& thePoints); + + /// \brief Return wrapper of PlaneGCS point + const PointWrapperPtr& value(const int theIndex) const + { return myPoints[theIndex]; } + + /// \breif Size of array + int size() const { return (int)myPoints.size(); } + + /// \brief Return type of current entity + virtual SketchSolver_EntityType type() const + { return ENTITY_POINT_ARRAY; } + +private: + std::vector myPoints; +}; + +typedef std::shared_ptr PointArrayWrapperPtr; + +#endif diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_ScalarArrayWrapper.cpp b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_ScalarArrayWrapper.cpp new file mode 100644 index 000000000..b599f8fbd --- /dev/null +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_ScalarArrayWrapper.cpp @@ -0,0 +1,25 @@ +// Copyright (C) 2019-2020 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 + +PlaneGCSSolver_ScalarArrayWrapper::PlaneGCSSolver_ScalarArrayWrapper(const GCS::VEC_pD& theParam) + : myValue(theParam) +{ +} diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_ScalarArrayWrapper.h b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_ScalarArrayWrapper.h new file mode 100644 index 000000000..40746cfb4 --- /dev/null +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_ScalarArrayWrapper.h @@ -0,0 +1,47 @@ +// Copyright (C) 2019-2020 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 +// + +#ifndef PlaneGCSSolver_ScalarArrayWrapper_H_ +#define PlaneGCSSolver_ScalarArrayWrapper_H_ + +#include +#include + +/** + * Wrapper providing operations with array of PlaneGCS scalars. + */ +class PlaneGCSSolver_ScalarArrayWrapper : public PlaneGCSSolver_EntityWrapper +{ +public: + PlaneGCSSolver_ScalarArrayWrapper(const GCS::VEC_pD& theParam); + + /// \brief Return array of PlaneGCS parameters + const GCS::VEC_pD& array() const { return myValue; } + + /// \brief Return type of current entity + virtual SketchSolver_EntityType type() const + { return ENTITY_SCALAR_ARRAY; } + +protected: + GCS::VEC_pD myValue; ///< list of pointers to values provided by the storage +}; + +typedef std::shared_ptr ScalarArrayWrapperPtr; + +#endif diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_ScalarWrapper.cpp b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_ScalarWrapper.cpp index 439bc8184..dc6d72ff3 100644 --- a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_ScalarWrapper.cpp +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_ScalarWrapper.cpp @@ -19,8 +19,6 @@ #include -#include - PlaneGCSSolver_ScalarWrapper::PlaneGCSSolver_ScalarWrapper(double *const theParam) : myValue(theParam) { diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.cpp b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.cpp index a5788f23a..9349f5c30 100644 --- a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.cpp +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.cpp @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include #include @@ -33,6 +35,8 @@ #include #include #include +#include +#include #include #include #include @@ -626,6 +630,23 @@ void PlaneGCSSolver_Storage::refresh() const } continue; } + std::shared_ptr aPointArray = + std::dynamic_pointer_cast(anIt->first); + if (aPointArray) { + std::shared_ptr anArrayWrapper = + std::dynamic_pointer_cast(anIt->second); + int aSize = aPointArray->size(); + for (int anIndex = 0; anIndex < aSize; ++anIndex) { + GeomPnt2dPtr anOriginal = aPointArray->pnt(anIndex); + GCSPointPtr aGCSPoint = anArrayWrapper->value(anIndex)->point(); + if (fabs(anOriginal->x() - (*aGCSPoint->x)) > aTol || + fabs(anOriginal->y() - (*aGCSPoint->y)) > aTol) { + aPointArray->setPnt(anIndex, *aGCSPoint->x, *aGCSPoint->y); + addOwnerToSet(anIt->first, anUpdatedFeatures); + } + } + continue; + } AttributeDoublePtr aScalar = std::dynamic_pointer_cast(anIt->first); if (aScalar) { ScalarWrapperPtr aScalarWrapper = @@ -636,6 +657,20 @@ void PlaneGCSSolver_Storage::refresh() const } continue; } + AttributeDoubleArrayPtr aRealArray = + std::dynamic_pointer_cast(anIt->first); + if (aRealArray) { + std::shared_ptr anArrayWrapper = + std::dynamic_pointer_cast(anIt->second); + int aSize = aRealArray->size(); + for (int anIndex = 0; anIndex < aSize; ++anIndex) { + if (fabs(aRealArray->value(anIndex) - *anArrayWrapper->array()[anIndex]) > aTol) { + aRealArray->setValue(anIndex, *anArrayWrapper->array()[anIndex]); + addOwnerToSet(anIt->first, anUpdatedFeatures); + } + } + continue; + } } // notify listeners about features update diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.cpp b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.cpp index a438391ab..c1b83d3f8 100644 --- a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.cpp +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.cpp @@ -191,6 +191,13 @@ std::shared_ptr PlaneGCSSolver_Tools::createMov new SketchSolver_ConstraintMovement(theMovedAttribute)); } +std::shared_ptr PlaneGCSSolver_Tools::createMovementConstraint( + const std::pair& theMovedPointInArray) +{ + return std::shared_ptr( + new SketchSolver_ConstraintMovement(theMovedPointInArray.first, theMovedPointInArray.second)); +} + ConstraintWrapperPtr PlaneGCSSolver_Tools::createConstraint( diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.h b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.h index a02a9948c..d90fecbe0 100644 --- a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.h +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.h @@ -45,6 +45,9 @@ namespace PlaneGCSSolver_Tools /// \brief Creates temporary constraint to fix the attribute after movement std::shared_ptr createMovementConstraint(AttributePtr theMovedAttribute); + /// \brief Creates temporary constraint to fix the point in array after movement + std::shared_ptr + createMovementConstraint(const std::pair& theMovedPointInArray); /// \brief Creates new constraint using given parameters /// \param theConstraint [in] original constraint diff --git a/src/SketchSolver/SketchSolver_Constraint.cpp b/src/SketchSolver/SketchSolver_Constraint.cpp index 48c0fa5b8..17720fdc4 100644 --- a/src/SketchSolver/SketchSolver_Constraint.cpp +++ b/src/SketchSolver/SketchSolver_Constraint.cpp @@ -233,7 +233,7 @@ void SketchSolver_Constraint::getAttributes( SketchSolver_EntityType aType = anEntity->type(); if (aType == ENTITY_UNKNOWN) continue; - else if (aType == ENTITY_POINT) + else if (aType == ENTITY_POINT || aType == ENTITY_POINT_ARRAY) theAttributes[aPtInd++] = anEntity; // the point is created else { // another entity (not a point) is created if (aEntInd < anInitNbOfAttr) diff --git a/src/SketchSolver/SketchSolver_ConstraintCoincidence.cpp b/src/SketchSolver/SketchSolver_ConstraintCoincidence.cpp index 4bac7dd1f..f4b616b9e 100644 --- a/src/SketchSolver/SketchSolver_ConstraintCoincidence.cpp +++ b/src/SketchSolver/SketchSolver_ConstraintCoincidence.cpp @@ -19,11 +19,14 @@ #include #include +#include #include #include #include +#include + #include #include #include @@ -186,6 +189,20 @@ static void processEllipticArcExtremities(SketchSolver_ConstraintType& theType, } } +static void getPointFromArray(EntityWrapperPtr& theArray, + const ConstraintPtr& theConstraint, + const std::string& theIndexAttrId) +{ + if (theArray && theArray->type() == ENTITY_POINT_ARRAY) { + AttributeIntegerPtr anIndexAttr = theConstraint->integer(theIndexAttrId); + if (anIndexAttr) { + PointArrayWrapperPtr aPointsArray = + std::dynamic_pointer_cast(theArray); + theArray = aPointsArray->value(anIndexAttr->value()); + } + } +} + void SketchSolver_ConstraintCoincidence::process() { @@ -244,6 +261,12 @@ void SketchSolver_ConstraintCoincidence::getAttributes( getCoincidentFeatureExtremities(myBaseConstraint, myStorage, myFeatureExtremities); } else myErrorMsg = SketchSolver_Error::INCORRECT_ATTRIBUTE(); + + // process internal coincidence with a point in the array of points + getPointFromArray(theAttributes[0], myBaseConstraint, + SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_A()); + getPointFromArray(theAttributes[1], myBaseConstraint, + SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B()); } void SketchSolver_ConstraintCoincidence::notify(const FeaturePtr& theFeature, diff --git a/src/SketchSolver/SketchSolver_ConstraintMovement.cpp b/src/SketchSolver/SketchSolver_ConstraintMovement.cpp index 8e38fa6b8..3384416c7 100644 --- a/src/SketchSolver/SketchSolver_ConstraintMovement.cpp +++ b/src/SketchSolver/SketchSolver_ConstraintMovement.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -53,12 +54,14 @@ SketchSolver_ConstraintMovement::SketchSolver_ConstraintMovement(FeaturePtr theF { } -SketchSolver_ConstraintMovement::SketchSolver_ConstraintMovement(AttributePtr thePoint) +SketchSolver_ConstraintMovement::SketchSolver_ConstraintMovement(AttributePtr theAttribute, + const int thePointIndex) : SketchSolver_ConstraintFixed(ConstraintPtr()), - myDraggedPoint(thePoint), + myDraggedAttribute(theAttribute), + myDraggedPointIndex(thePointIndex), mySimpleMove(true) { - myMovedFeature = ModelAPI_Feature::feature(thePoint->owner()); + myMovedFeature = ModelAPI_Feature::feature(theAttribute->owner()); } void SketchSolver_ConstraintMovement::blockEvents(bool isBlocked) @@ -115,22 +118,27 @@ ConstraintWrapperPtr SketchSolver_ConstraintMovement::initMovement() return aConstraint; } - EntityWrapperPtr anEntity = - myDraggedPoint ? myStorage->entity(myDraggedPoint) : myStorage->entity(myMovedFeature); + EntityWrapperPtr anEntity = myDraggedAttribute ? myStorage->entity(myDraggedAttribute) + : myStorage->entity(myMovedFeature); if (!anEntity) { myStorage->update(myMovedFeature, true); - anEntity = - myDraggedPoint ? myStorage->entity(myDraggedPoint) : myStorage->entity(myMovedFeature); + anEntity = myDraggedAttribute ? myStorage->entity(myDraggedAttribute) + : myStorage->entity(myMovedFeature); if (!anEntity) return aConstraint; } - mySimpleMove = isSimpleMove(myMovedFeature, myDraggedPoint); + mySimpleMove = isSimpleMove(myMovedFeature, myDraggedAttribute); - if (mySimpleMove) + if (mySimpleMove) { + if (anEntity->type() == ENTITY_POINT_ARRAY) { + anEntity = std::dynamic_pointer_cast(anEntity) + ->value(myDraggedPointIndex); + } aConstraint = fixFeature(anEntity); + } else { - if (myDraggedPoint) // start or end point of arc has been moved + if (myDraggedAttribute) // start or end point of arc has been moved aConstraint = fixArcExtremity(anEntity); else if (anEntity->type() == ENTITY_CIRCLE || anEntity->type() == ENTITY_ARC) { // arc or circle has been moved @@ -311,7 +319,8 @@ void SketchSolver_ConstraintMovement::moveTo( #ifdef CHANGE_RADIUS_WHILE_MOVE int aMaxSize = mySimpleMove ? (int)myFixedValues.size() : 2; #else - int aMaxSize = myMovedFeature->getKind() == SketchPlugin_Line::ID() && !myDraggedPoint ? 4 : 2; + int aMaxSize = + myMovedFeature->getKind() == SketchPlugin_Line::ID() && !myDraggedAttribute ? 4 : 2; #endif for (int i = 0; i < aMaxSize; ++i) myFixedValues[i] += aDelta[i % 2]; diff --git a/src/SketchSolver/SketchSolver_ConstraintMovement.h b/src/SketchSolver/SketchSolver_ConstraintMovement.h index bfbe1f1ed..451ddb462 100644 --- a/src/SketchSolver/SketchSolver_ConstraintMovement.h +++ b/src/SketchSolver/SketchSolver_ConstraintMovement.h @@ -39,7 +39,7 @@ public: SketchSolver_ConstraintMovement(FeaturePtr theFeature); /// Creates movement constraint based on point - SketchSolver_ConstraintMovement(AttributePtr thePoint); + SketchSolver_ConstraintMovement(AttributePtr theMovedAttribute, const int thePointIndex = -1); /// \brief Set coordinates of the start point of the movement void startPoint(const std::shared_ptr& theStartPoint); @@ -74,7 +74,8 @@ protected: private: FeaturePtr myMovedFeature; ///< fixed feature (if set, myBaseConstraint should be NULL) - AttributePtr myDraggedPoint; ///< one of the feature points which has been moved + AttributePtr myDraggedAttribute; ///< one of the feature points which has been moved + int myDraggedPointIndex; ///< index of the point if the moved attribute is an array std::shared_ptr myStartPoint; ///< start point of the movement bool mySimpleMove; ///< simple move, thus all parameters should be increased by movement delta diff --git a/src/SketchSolver/SketchSolver_Group.cpp b/src/SketchSolver/SketchSolver_Group.cpp index 96c0a2968..4f1f5ee86 100644 --- a/src/SketchSolver/SketchSolver_Group.cpp +++ b/src/SketchSolver/SketchSolver_Group.cpp @@ -164,10 +164,11 @@ static SolverConstraintPtr move(StoragePtr theStorage, int theSketchDOF, bool theEventsBlocked, Type theFeatureOrPoint, + const EntityWrapperPtr& theSolverEntity, const std::shared_ptr& theFrom, const std::shared_ptr& theTo) { - bool isEntityExists = (theStorage->entity(theFeatureOrPoint).get() != 0); + bool isEntityExists = (theSolverEntity.get() != 0); if (theSketchDOF == 0 && isEntityExists) { // avoid moving elements of fully constrained sketch theStorage->refresh(); @@ -196,18 +197,29 @@ bool SketchSolver_Group::moveFeature(FeaturePtr theFeature, const std::shared_ptr& theFrom, const std::shared_ptr& theTo) { - SolverConstraintPtr aConstraint = - move(myStorage, mySketchSolver, myDOF, myIsEventsBlocked, theFeature, theFrom, theTo); + EntityWrapperPtr anEntity = myStorage->entity(theFeature); + SolverConstraintPtr aConstraint = move(myStorage, mySketchSolver, myDOF, myIsEventsBlocked, + theFeature, anEntity, theFrom, theTo); setTemporary(aConstraint); return true; } bool SketchSolver_Group::movePoint(AttributePtr theAttribute, + const int thePointIndex, const std::shared_ptr& theFrom, const std::shared_ptr& theTo) { - SolverConstraintPtr aConstraint = - move(myStorage, mySketchSolver, myDOF, myIsEventsBlocked, theAttribute, theFrom, theTo); + EntityWrapperPtr anEntity = myStorage->entity(theAttribute); + SolverConstraintPtr aConstraint; + if (thePointIndex < 0) { + aConstraint = move(myStorage, mySketchSolver, myDOF, myIsEventsBlocked, + theAttribute, anEntity, theFrom, theTo); + } + else { + aConstraint = move(myStorage, mySketchSolver, myDOF, myIsEventsBlocked, + std::pair(theAttribute, thePointIndex), anEntity, + theFrom, theTo); + } setTemporary(aConstraint); return true; } diff --git a/src/SketchSolver/SketchSolver_Group.h b/src/SketchSolver/SketchSolver_Group.h index 45407ddb8..d56171559 100644 --- a/src/SketchSolver/SketchSolver_Group.h +++ b/src/SketchSolver/SketchSolver_Group.h @@ -90,12 +90,14 @@ class SketchSolver_Group const std::shared_ptr& theTo); /** \brief Updates the data corresponding the specified point moved in GUI. * Special kind of Fixed constraints is created. - * \param[in] thePoint the attribute to be updated - * \param[in] theFrom start point of the movement - * \param[in] theTo final point of the movement + * \param[in] thePointOrArray the attribute to be updated + * \param[in] thePointIndex index of moved point in array + * \param[in] theFrom start point of the movement + * \param[in] theTo destination point of the movement * \return \c true, if the attribute is really moved */ - bool movePoint(AttributePtr thePoint, + bool movePoint(AttributePtr thePointOrArray, + const int thePointIndex, const std::shared_ptr& theFrom, const std::shared_ptr& theTo); diff --git a/src/SketchSolver/SketchSolver_Manager.cpp b/src/SketchSolver/SketchSolver_Manager.cpp index a9d47533e..b7b6d4980 100644 --- a/src/SketchSolver/SketchSolver_Manager.cpp +++ b/src/SketchSolver/SketchSolver_Manager.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -87,6 +88,19 @@ static void featuresOrderedByType(const std::set& theOriginalFeatures } } +static void setPoint(AttributePtr theAttribute, + const int thePointIndex, + const std::shared_ptr theValue) +{ + AttributePoint2DPtr aPointAttr = std::dynamic_pointer_cast(theAttribute); + AttributePoint2DArrayPtr aPointArrayAttr = + std::dynamic_pointer_cast(theAttribute); + if (aPointAttr) + aPointAttr->setValue(theValue); + else if (aPointArrayAttr && thePointIndex >= 0) + aPointArrayAttr->setPnt(thePointIndex, theValue); +} + // ======================================================== @@ -180,8 +194,8 @@ void SketchSolver_Manager::processEvent( std::dynamic_pointer_cast(theMessage); ObjectPtr aMovedObject = aMoveMsg->movedObject(); - std::shared_ptr aMovedPoint = - std::dynamic_pointer_cast(aMoveMsg->movedAttribute()); + AttributePtr aMovedAttribute = aMoveMsg->movedAttribute(); + int aMovedPoint = aMoveMsg->movedPointIndex(); const std::shared_ptr& aFrom = aMoveMsg->originalPosition(); const std::shared_ptr& aTo = aMoveMsg->currentPosition(); @@ -192,8 +206,8 @@ void SketchSolver_Manager::processEvent( std::dynamic_pointer_cast(aMovedFeature); if (aSketchFeature && !aSketchFeature->isMacro()) needToResolve = moveFeature(aSketchFeature, aFrom, aTo); - } else if (aMovedPoint) - needToResolve = moveAttribute(aMovedPoint, aFrom, aTo); + } else if (aMovedAttribute) + needToResolve = moveAttribute(aMovedAttribute, aMovedPoint, aFrom, aTo); } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) { std::shared_ptr aDeleteMsg = @@ -349,7 +363,8 @@ bool SketchSolver_Manager::moveFeature( // Purpose: move given attribute in appropriate group // ============================================================================ bool SketchSolver_Manager::moveAttribute( - const std::shared_ptr& theMovedAttribute, + const std::shared_ptr& theMovedAttribute, + const int theMovedPointIndex, const std::shared_ptr& theFrom, const std::shared_ptr& theTo) { @@ -358,7 +373,7 @@ bool SketchSolver_Manager::moveAttribute( std::dynamic_pointer_cast(anOwner); if (aConstraint) { - theMovedAttribute->setValue(theTo); + setPoint(theMovedAttribute, theMovedPointIndex, theTo); Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED)); return true; } @@ -369,12 +384,12 @@ bool SketchSolver_Manager::moveAttribute( if (aSketchFeature) aGroup = findGroup(aSketchFeature); if (!aGroup) { - theMovedAttribute->setValue(theTo); + setPoint(theMovedAttribute, theMovedPointIndex, theTo); return false; } aGroup->blockEvents(true); - return aGroup->movePoint(theMovedAttribute, theFrom, theTo); + return aGroup->movePoint(theMovedAttribute, theMovedPointIndex, theFrom, theTo); } // ============================================================================ diff --git a/src/SketchSolver/SketchSolver_Manager.h b/src/SketchSolver/SketchSolver_Manager.h index 78ddaefbb..33aa152a5 100644 --- a/src/SketchSolver/SketchSolver_Manager.h +++ b/src/SketchSolver/SketchSolver_Manager.h @@ -85,12 +85,14 @@ protected: const std::shared_ptr& theToPoint); /** \brief Move feature using its moved attribute - * \param[in] theMovedAttribute dragged point attribute of sketch feature - * \param[in] theFromPoint original position of the moved point - * \param[in] theToPoint prefereble position (current position of the mouse) + * \param[in] theMovedAttribute dragged point (array of points) attribute of sketch feature + * \param[in] theMovedPointIndex index of dragged point in an array (-1 otherwise) + * \param[in] theFromPoint original position of the moved point + * \param[in] theToPoint prefereble position (current position of the mouse) * \return \c true if the attribute owner has been changed successfully */ - bool moveAttribute(const std::shared_ptr& theMovedAttribute, + bool moveAttribute(const std::shared_ptr& theMovedAttribute, + const int theMovedPointIndex, const std::shared_ptr& theFromPoint, const std::shared_ptr& theToPoint);